国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Promise的源碼實現(xiàn)(完美符合Promise/A+規(guī)范)

gaomysion / 1136人閱讀

摘要:以上代碼,可以完美通過所有用例。在的函數(shù)中,為何需要這個同樣是因為規(guī)范中明確表示因此我們需要這樣的來確保只會執(zhí)行一次。其他情況,直接返回以該值為成功狀態(tài)的對象。

Promise是前端面試中的高頻問題,我作為面試官的時候,問Promise的概率超過90%,據(jù)我所知,大多數(shù)公司,都會問一些關(guān)于Promise的問題。如果你能根據(jù)PromiseA+的規(guī)范,寫出符合規(guī)范的源碼,那么我想,對于面試中的Promise相關(guān)的問題,都能夠給出比較完美的答案。

我的建議是,對照規(guī)范多寫幾次實現(xiàn),也許第一遍的時候,是改了多次,才能通過測試,那么需要反復(fù)的寫,我已經(jīng)將Promise的源碼實現(xiàn)寫了不下七遍。

Promise的源碼實現(xiàn)
/**
 * 1. new Promise時,需要傳遞一個 executor 執(zhí)行器,執(zhí)行器立刻執(zhí)行
 * 2. executor 接受兩個參數(shù),分別是 resolve 和 reject
 * 3. promise 只能從 pending 到 rejected, 或者從 pending 到 fulfilled
 * 4. promise 的狀態(tài)一旦確認,就不會再改變
 * 5. promise 都有 then 方法,then 接收兩個參數(shù),分別是 promise 成功的回調(diào) onFulfilled, 
 *      和 promise 失敗的回調(diào) onRejected
 * 6. 如果調(diào)用 then 時,promise已經(jīng)成功,則執(zhí)行 onFulfilled,并將promise的值作為參數(shù)傳遞進去。
 *      如果promise已經(jīng)失敗,那么執(zhí)行 onRejected, 并將 promise 失敗的原因作為參數(shù)傳遞進去。
 *      如果promise的狀態(tài)是pending,需要將onFulfilled和onRejected函數(shù)存放起來,等待狀態(tài)確定后,再依次將對應(yīng)的函數(shù)執(zhí)行(發(fā)布訂閱)
 * 7. then 的參數(shù) onFulfilled 和 onRejected 可以缺省
 * 8. promise 可以then多次,promise 的then 方法返回一個 promise
 * 9. 如果 then 返回的是一個結(jié)果,那么就會把這個結(jié)果作為參數(shù),傳遞給下一個then的成功的回調(diào)(onFulfilled)
 * 10. 如果 then 中拋出了異常,那么就會把這個異常作為參數(shù),傳遞給下一個then的失敗的回調(diào)(onRejected)
 * 11.如果 then 返回的是一個promise,那么需要等這個promise,那么會等這個promise執(zhí)行完,promise如果成功,
 *   就走下一個then的成功,如果失敗,就走下一個then的失敗
 */

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
function Promise(executor) {
    let self = this;
    self.status = PENDING;
    self.onFulfilled = [];//成功的回調(diào)
    self.onRejected = []; //失敗的回調(diào)
    //PromiseA+ 2.1
    function resolve(value) {
        if (self.status === PENDING) {
            self.status = FULFILLED;
            self.value = value;
            self.onFulfilled.forEach(fn => fn());//PromiseA+ 2.2.6.1
        }
    }

    function reject(reason) {
        if (self.status === PENDING) {
            self.status = REJECTED;
            self.reason = reason;
            self.onRejected.forEach(fn => fn());//PromiseA+ 2.2.6.2
        }
    }

    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}

Promise.prototype.then = function (onFulfilled, onRejected) {
    //PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
    onRejected = typeof onRejected === "function" ? onRejected : reason => { throw reason };
    let self = this;
    //PromiseA+ 2.2.7
    let promise2 = new Promise((resolve, reject) => {
        if (self.status === FULFILLED) {
            //PromiseA+ 2.2.2
            //PromiseA+ 2.2.4 --- setTimeout
            setTimeout(() => {
                try {
                    //PromiseA+ 2.2.7.1
                    let x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    //PromiseA+ 2.2.7.2
                    reject(e);
                }
            });
        } else if (self.status === REJECTED) {
            //PromiseA+ 2.2.3
            setTimeout(() => {
                try {
                    let x = onRejected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            });
        } else if (self.status === PENDING) {
            self.onFulfilled.push(() => {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                });
            });
            self.onRejected.push(() => {
                setTimeout(() => {
                    try {
                        let x = onRejected(self.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                });
            });
        }
    });
    return promise2;
}

function resolvePromise(promise2, x, resolve, reject) {
    let self = this;
    //PromiseA+ 2.3.1
    if (promise2 === x) {
        reject(new TypeError("Chaining cycle"));
    }
    if (x && typeof x === "object" || typeof x === "function") {
        let used; //PromiseA+2.3.3.3.3 只能調(diào)用一次
        try {
            let then = x.then;
            if (typeof then === "function") {
                //PromiseA+2.3.3
                then.call(x, (y) => {
                    //PromiseA+2.3.3.1
                    if (used) return;
                    used = true;
                    resolvePromise(promise2, y, resolve, reject);
                }, (r) => {
                    //PromiseA+2.3.3.2
                    if (used) return;
                    used = true;
                    reject(r);
                });

            }else{
                //PromiseA+2.3.3.4
                if (used) return;
                used = true;
                resolve(x);
            }
        } catch (e) {
            //PromiseA+ 2.3.3.2
            if (used) return;
            used = true;
            reject(e);
        }
    } else {
        //PromiseA+ 2.3.3.4
        resolve(x);
    }
}

module.exports = Promise;

有專門的測試腳本可以測試所編寫的代碼是否符合PromiseA+的規(guī)范。

首先,在promise實現(xiàn)的代碼中,增加以下代碼:

Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
}

安裝測試腳本:

npm install -g promises-aplus-tests

如果當前的promise源碼的文件名為promise.js

那么在對應(yīng)的目錄執(zhí)行以下命令:

promises-aplus-tests promise.js

promises-aplus-tests中共有872條測試用例。以上代碼,可以完美通過所有用例。

對上面的代碼實現(xiàn)做一點簡要說明(其它一些內(nèi)容注釋中已經(jīng)寫得很清楚):

onFulfilled 和 onFulfilled的調(diào)用需要放在setTimeout,因為規(guī)范中表示: onFulfilled or onRejected must not be called until the execution context stack contains only platform code。使用setTimeout只是模擬異步,原生Promise并非是這樣實現(xiàn)的。

在 resolvePromise 的函數(shù)中,為何需要usedd這個flag,同樣是因為規(guī)范中明確表示: If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored. 因此我們需要這樣的flag來確保只會執(zhí)行一次。

self.onFulfilled 和 self.onRejected 中存儲了成功的回調(diào)和失敗的回調(diào),根據(jù)規(guī)范2.6顯示,當promise從pending態(tài)改變的時候,需要按照順序去指定then對應(yīng)的回調(diào)。

PromiseA+的規(guī)范(翻譯版)

PS: 下面是我翻譯的規(guī)范,供參考

術(shù)語

promise 是一個有then方法的對象或者是函數(shù),行為遵循本規(guī)范

thenable 是一個有then方法的對象或者是函數(shù)

value 是promise狀態(tài)成功時的值,包括 undefined/thenable或者是 promise

exception 是一個使用throw拋出的異常值

reason 是promise狀態(tài)失敗時的值

要求
2.1 Promise States

Promise 必須處于以下三個狀態(tài)之一: pending, fulfilled 或者是 rejected

2.1.1 如果promise在pending狀態(tài)
2.1.1.1 可以變成 fulfilled 或者是 rejected
2.1.2 如果promise在fulfilled狀態(tài)
2.1.2.1 不會變成其它狀態(tài)

2.1.2.2 必須有一個value值
2.1.3 如果promise在rejected狀態(tài)
2.1.3.1 不會變成其它狀態(tài)

2.1.3.2 必須有一個promise被reject的reason

概括即是:promise的狀態(tài)只能從pending變成fulfilled,或者從pending變成rejected.promise成功,有成功的value.promise失敗的話,有失敗的原因

2.2 then方法

promise必須提供一個then方法,來訪問最終的結(jié)果

promise的then方法接收兩個參數(shù)

promise.then(onFulfilled, onRejected)
2.2.1 onFulfilled 和 onRejected 都是可選參數(shù)
2.2.1.1 onFulfilled 必須是函數(shù)類型

2.2.1.2 onRejected 必須是函數(shù)類型
2.2.2 如果 onFulfilled 是函數(shù):
2.2.2.1 必須在promise變成 fulfilled 時,調(diào)用 onFulfilled,參數(shù)是promise的value
2.2.2.2 在promise的狀態(tài)不是 fulfilled 之前,不能調(diào)用
2.2.2.3 onFulfilled 只能被調(diào)用一次
2.2.3 如果 onRejected 是函數(shù):
2.2.3.1 必須在promise變成 rejected 時,調(diào)用 onRejected,參數(shù)是promise的reason
2.2.3.2 在promise的狀態(tài)不是 rejected 之前,不能調(diào)用
2.2.3.3 onRejected 只能被調(diào)用一次
2.2.4 onFulfilled 和 onRejected 應(yīng)該是微任務(wù)
2.2.5 onFulfilled 和 onRejected 必須作為函數(shù)被調(diào)用
2.2.6 then方法可能被多次調(diào)用
2.2.6.1 如果promise變成了 fulfilled態(tài),所有的onFulfilled回調(diào)都需要按照then的順序執(zhí)行
2.2.6.2 如果promise變成了 rejected態(tài),所有的onRejected回調(diào)都需要按照then的順序執(zhí)行
2.2.7 then必須返回一個promise
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1 onFulfilled 或 onRejected 執(zhí)行的結(jié)果為x,調(diào)用 resolvePromise
2.2.7.2 如果 onFulfilled 或者 onRejected 執(zhí)行時拋出異常e,promise2需要被reject
2.2.7.3 如果 onFulfilled 不是一個函數(shù),promise2 以promise1的值fulfilled
2.2.7.4 如果 onRejected 不是一個函數(shù),promise2 以promise1的reason rejected
2.3 resolvePromise

resolvePromise(promise2, x, resolve, reject)

2.3.1 如果 promise2 和 x 相等,那么 reject promise with a TypeError
2.3.2 如果 x 是一個 promsie
2.3.2.1 如果x是pending態(tài),那么promise必須要在pending,直到 x 變成 fulfilled or rejected.
2.3.2.2 如果 x 被 fulfilled, fulfill promise with the same value.
2.3.2.3 如果 x 被 rejected, reject promise with the same reason.
2.3.3 如果 x 是一個 object 或者 是一個 function
2.3.3.1 let then = x.then.
2.3.3.2 如果 x.then 這步出錯,那么 reject promise with e as the reason..
2.3.3.3 如果 then 是一個函數(shù),then.call(x, resolvePromiseFn, rejectPromise)
    2.3.3.3.1 resolvePromiseFn 的 入?yún)⑹?y, 執(zhí)行 resolvePromise(promise2, y, resolve, reject);
    2.3.3.3.2 rejectPromise 的 入?yún)⑹?r, reject promise with r.
    2.3.3.3.3 如果 resolvePromise 和 rejectPromise 都調(diào)用了,那么第一個調(diào)用優(yōu)先,后面的調(diào)用忽略。
    2.3.3.3.4 如果調(diào)用then拋出異常e 
        2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已經(jīng)被調(diào)用,那么忽略
        2.3.3.3.4.3 否則,reject promise with e as the reason
2.3.3.4 如果 then 不是一個function. fulfill promise with x.
2.3.4 如果 x 不是一個 object 或者 function,fulfill promise with x.
Promise的其他方法

雖然上述的promise源碼已經(jīng)符合PromiseA+的規(guī)范,但是原生的Promise還提供了一些其他方法,如:

Promise.resolve()

Promise.reject()

Promise.prototype.catch()

Promise.prototype.finally()

Promise.all()

Promise.race()

下面具體說一下每個方法的實現(xiàn):

Promise.resolve

Promise.resolve(value) 返回一個以給定值解析后的Promise 對象.

如果 value 是個 thenable 對象,返回的promise會“跟隨”這個thenable的對象,采用它的最終狀態(tài)

如果傳入的value本身就是promise對象,那么Promise.resolve將不做任何修改、原封不動地返回這個promise對象。

其他情況,直接返回以該值為成功狀態(tài)的promise對象。

Promise.resolve = function (param) {
        if (param instanceof Promise) {
        return param;
    }
    return new Promise((resolve, reject) => {
        if (param && param.then && typeof param.then === "function") {
            setTimeout(() => {
                param.then(resolve, reject);
            });
        } else {
            resolve(param);
        }
    });
}

thenable對象的執(zhí)行加 setTimeout的原因是根據(jù)原生Promise對象執(zhí)行的結(jié)果推斷的,如下的測試代碼,原生的執(zhí)行結(jié)果為: 20 400 30;為了同樣的執(zhí)行順序,增加了setTimeout延時。

測試代碼:

let p = Promise.resolve(20);
p.then((data) => {
    console.log(data);
});


let p2 = Promise.resolve({
    then: function(resolve, reject) {
        resolve(30);
    }
});

p2.then((data)=> {
    console.log(data)
});

let p3 = Promise.resolve(new Promise((resolve, reject) => {
    resolve(400)
}));
p3.then((data) => {
    console.log(data)
});
Promise.reject

Promise.reject方法和Promise.resolve不同,Promise.reject()方法的參數(shù),會原封不動地作為reject的理由,變成后續(xù)方法的參數(shù)。

Promise.reject = function (reason) {
    return new Promise((resolve, reject) => {
        reject(reason);
    });
}
Promise.prototype.catch

Promise.prototype.catch 用于指定出錯時的回調(diào),是特殊的then方法,catch之后,可以繼續(xù) .then

Promise.prototype.catch = function (onRejected) {
    return this.then(null, onRejected);
}
Promise.prototype.finally

不管成功還是失敗,都會走到finally中,并且finally之后,還可以繼續(xù)then。并且會將值原封不動的傳遞給后面的then.

Promise.prototype.finally = function (callback) {
    return this.then((value) => {
        return Promise.resolve(callback()).then(() => {
            return value;
        });
    }, (err) => {
        return Promise.resolve(callback()).then(() => {
            throw err;
        });
    });
}
Promise.all

Promise.all(promises) 返回一個promise對象

如果傳入的參數(shù)是一個空的可迭代對象,那么此promise對象回調(diào)完成(resolve),只有此情況,是同步執(zhí)行的,其它都是異步返回的。

如果傳入的參數(shù)不包含任何 promise,則返回一個異步完成.

promises 中所有的promise都promise都“完成”時或參數(shù)中不包含 promise 時回調(diào)完成。

如果參數(shù)中有一個promise失敗,那么Promise.all返回的promise對象失敗

在任何情況下,Promise.all 返回的 promise 的完成狀態(tài)的結(jié)果都是一個數(shù)組

Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let index = 0;
        let result = [];
        if (promises.length === 0) {
            resolve(result);
        } else {
            function processValue(i, data) {
                result[i] = data;
                if (++index === promises.length) {
                    resolve(result);
                }
            }
            for (let i = 0; i < promises.length; i++) {
                    //promises[i] 可能是普通值
                    Promise.resolve(promises[i]).then((data) => {
                    processValue(i, data);
                }, (err) => {
                    reject(err);
                    return;
                });
            }
        }
    });
}

測試代碼:

var promise1 = new Promise((resolve, reject) => {
    resolve(3);
})
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, "foo");
});

Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values); //[3, 42, "foo"]
},(err)=>{
    console.log(err)
});

var p = Promise.all([]); // will be immediately resolved
var p2 = Promise.all([1337, "hi"]); // non-promise values will be ignored, but the evaluation will be done asynchronously
console.log(p);
console.log(p2)
setTimeout(function(){
    console.log("the stack is now empty");
    console.log(p2);
});
Promise.race

Promise.race函數(shù)返回一個 Promise,它將與第一個傳遞的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失?。╮ejects),這要取決于第一個完成的方式是兩個中的哪個。

如果傳的參數(shù)數(shù)組是空,則返回的 promise 將永遠等待。

如果迭代包含一個或多個非承諾值和/或已解決/拒絕的承諾,則 Promise.race 將解析為迭代中找到的第一個值。

Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        if (promises.length === 0) {
            return;
        } else {
            for (let i = 0; i < promises.length; i++) {
                Promise.resolve(promises[i]).then((data) => {
                    resolve(data);
                    return;
                }, (err) => {
                    reject(err);
                    return;
                });
            }
        }
    });
}

測試代碼:

Promise.race([
    new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }),
    undefined,
    new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) })
]).then((data) => {
    console.log("success ", data);
}, (err) => {
    console.log("err ",err);
});

Promise.race([
    new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }),
    new Promise((resolve, reject) => { setTimeout(() => { resolve(200) }, 200) }),
    new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) })
]).then((data) => {
    console.log(data);
}, (err) => {
    console.log(err);
});

謝謝您花費寶貴的時間閱讀本文,如果本文給了您一點幫助或者是啟發(fā),那么不要吝嗇你的贊和Star哈,您的肯定是我前進的最大動力。https://github.com/YvetteLau/...

關(guān)注小姐姐的公眾號,和小姐姐一起學前端。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/108987.html

相關(guān)文章

  • ES6-7

    摘要:的翻譯文檔由的維護很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...

    mudiyouyou 評論0 收藏0
  • 手寫一款符合Promise/A+規(guī)范Promise

    摘要:手寫一款符合規(guī)范的長篇預(yù)警有點長,可以選擇性觀看。初始狀態(tài)是,狀態(tài)可以有或者不能從轉(zhuǎn)換為或者從轉(zhuǎn)換成即只要由狀態(tài)轉(zhuǎn)換為其他狀態(tài)后,狀態(tài)就不可變更。 手寫一款符合Promise/A+規(guī)范的Promise 長篇預(yù)警!有點長,可以選擇性觀看。如果對Promise源碼不是很清楚,還是推薦從頭看,相信你認真從頭看到尾,并且去實際操作了,肯定會有收獲的。主要是代碼部分有點多,不過好多都是重復(fù)的,不...

    rubyshen 評論0 收藏0
  • promise/A+規(guī)范翻譯以及手寫實現(xiàn)

    摘要:如果實現(xiàn)滿足所有要求,則實現(xiàn)可能允許。本條款允許使用特定于實現(xiàn)的方法來采用已知一致承諾的狀態(tài)。接下來根據(jù)規(guī)范進行手寫實現(xiàn)注釋偷懶就將對應(yīng)的規(guī)范標注出來,其實基本上就是對著規(guī)范實現(xiàn)。 如果要手寫實現(xiàn)promise,那么先看看promise/A+規(guī)范,再來實現(xiàn),將會事半功倍。那么我先翻譯一下Promise/A+規(guī)范中的內(nèi)容。 術(shù)語 1.1 promise 是一個帶有符合此規(guī)范的the...

    LiuZh 評論0 收藏0
  • JavaScript 異步

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個符合規(guī)范并可配合使用的寫一個符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個需求:在系統(tǒng)初始化時通過http獲取一個第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個接口,可通過...

    tuniutech 評論0 收藏0
  • 讀Zepto源碼之Deferred模塊

    摘要:為的項,取出來的分別為和,所以上的和方法,調(diào)用的是中的方法,實質(zhì)是往各自的回調(diào)列表中添加回調(diào)函數(shù)。進度回調(diào)函數(shù)數(shù)組。參數(shù)為異步對象的索引值,參數(shù)為對應(yīng)的上下文數(shù)組,即或,為對應(yīng)的回調(diào)函數(shù)數(shù)組,即或。 Deferred 模塊也不是必備的模塊,但是 ajax 模塊中,要用到 promise 風格,必需引入 Deferred 模塊。Deferred 也用到了上一篇文章《讀Zepto源碼之C...

    yagami 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<