實(shí)現(xiàn)一個(gè)符合 Promise/A+ 規(guī)范的 MyPromise

Promise

實(shí)現(xiàn)一個(gè)符合 Promise/A+ 規(guī)范的 MyPromise秒际,并實(shí)現(xiàn) resolve悬赏、reject、all娄徊、race闽颇、defer、deferred等靜態(tài)方法寄锐。

MyPromise

  1. 作用:創(chuàng)建 MyPromise實(shí)例兵多。Promise

  2. MyPromise接收一個(gè)回掉函數(shù) executor

  3. MyPromise狀態(tài)

    • pending

      • 可以轉(zhuǎn)換成 fulfilled 或 rejected
    • fulfilled

      • 不可改變成其他狀態(tài)
    • rejected

      • 不可改變成其他狀態(tài)
  4. onFulfilledCallbacksonRejectedCallbacks

    • 兩個(gè)數(shù)組,數(shù)組每一項(xiàng)是一個(gè)函數(shù)橄仆。分別接收then里面的第一個(gè)參數(shù)和第二個(gè)參數(shù)剩膘。

    • 狀態(tài)是 pending 的回掉函數(shù)。

  5. resolve

    • promise的狀態(tài)是fulfilled異常是的處理函數(shù)

    • 接收 value 參數(shù)

      • 如果是promise盆顾,執(zhí)行then怠褐。

      • 如果不是promise,把value做為參數(shù)傳給onFulfilledCallbacks里的每個(gè)函數(shù)您宪。

  6. reject

    • promise的狀態(tài)是rejected異常是的處理函數(shù)

    • 接收 reason 參數(shù)奈懒,把reason做為參數(shù)傳給onRejectedCallbacks里的每個(gè)函數(shù)奠涌。

  7. 執(zhí)行 executor,如果有異常筐赔,拋給reject

  8. 因?yàn)?code>Promise是在同步代碼執(zhí)行完成后再執(zhí)行铣猩,所以要把Mypromise的執(zhí)行方法resolvereject放在異步隊(duì)列里


function MyPromise(executor) {

    if (typeof executor !== 'function') {

        throw new TypeError('Promise resolver ' + executor + ' is not a function');

    }

    let self = this;

    this.status = 'pending';

    this.value = undefined;

    this.reason = undefined;

    this.onFulfilledCallbacks = [];

    this.onRejectedCallbacks = [];

    function resolve(value) {
        if (value instanceof MyPromise) {
            return value.then(resolve, reject);
        }
        if (self.status === 'pending') {            
            self.value = value;
            self.status = 'fulfilled';
            self.onFulfilledCallbacks.forEach(item => item(value));
        }
    }
    
    function reject(reason) {
        if (self.status === 'pending') {
            self.reason = reason;
            self.status = 'rejected';
            self.onRejectedCallbacks.forEach(item => item(reason));
        }
    }


    try {

        executor(resolve, reject);

    } catch (e) {

        reject(e);

    }

}

MyPromise.prototype.then

  1. 作用:接收兩個(gè)函數(shù)參數(shù),第一個(gè)函數(shù)的參數(shù)是 resolve傳入的參數(shù)茴丰,第二個(gè)參數(shù)是 reject傳入的參數(shù)达皿。Promise#then

  2. onFulfilled

    • MyPromise 成功時(shí)執(zhí)行的方法

    • resolve 的參數(shù)會(huì)作為value傳給 onFulfilled

  3. onRejected

    • MyPromise 失敗時(shí)執(zhí)行的方法

    • reject 的參數(shù)會(huì)作為value傳給 onRejected

  4. 返回一個(gè) MyPromise 實(shí)例 newPromise,方便鏈?zhǔn)秸{(diào)用

  5. 對(duì)三種狀態(tài)分別處理

    • 每個(gè)狀態(tài)中創(chuàng)建 newPromise

    • fulfilled

      • 直接執(zhí)行 onFulfilled贿肩,返回值x

      • newPromise峦椰、x以及newPromise里的resolvereject做為參數(shù)傳給 resolutionPromise

      • 把 MyPromise 的參數(shù)放在異步隊(duì)列里

    • rejected

      • 直接執(zhí)行 onRejected汰规,返回值x

      • newPromise汤功、x以及newPromise里的resolvereject做為參數(shù)傳給 resolutionPromise

      • 把 MyPromise 的參數(shù)放在異步隊(duì)列里

    • pending

      • 狀態(tài)待定溜哮,把fulfilledrejected里的異步函數(shù)分別加到 onFulfilledCallbacksonRejectedCallbacks的最后一位
  6. resolutionPromise 后面細(xì)說

  7. catch捕獲異常滔金,執(zhí)行 reject


MyPromise.prototype.then = function (onFulfilled, onRejected) {

    let self = this;

    typeof onFulfilled !== 'function' && (onFulfilled = function (value) {

        return value;

    });

    typeof onRejected !== 'function' && (onRejected = function (reason) {

        throw reason;

    });

    let newPromise;

    /**

    *  分別處理實(shí)例的三種狀態(tài)

    */

    if (self.status === 'fulfilled') {

        newPromise = new MyPromise(function (resolve, reject) {

            setTimeout(function () {

                try {

                    let x = onFulfilled(self.value);

                    resolutionPromise(newPromise, x, resolve, reject);

                } catch (e) {

                    reject(e);

                }

            });

        });

    }

    if (self.status === 'rejected') {

        newPromise = new MyPromise(function (resolve, reject) {

            setTimeout(function () {

                try {

                    let x = onRejected(self.reason);

                    resolutionPromise(newPromise, x, resolve, reject);

                } catch (e) {

                    reject(e);

                }

            });

        });

    }

    if (self.status === 'pending') {

        newPromise = new MyPromise(function (resolve, reject) {

            self.onFulfilledCallbacks.push(function (value) {

                setTimeout(function () {

                    try {

                        let x = onFulfilled(value);

                        resolutionPromise(newPromise, x, resolve, reject);

                    } catch (e) {

                        reject(e);

                    }

                });

            });

            self.onRejectedCallbacks.push(function (reason) {

                setTimeout(function () {

                    try {

                        let x = onRejected(reason);

                        resolutionPromise(newPromise, x, resolve, reject);

                    } catch (e) {

                        reject(e);

                    }

                });

            });

        });

    }

    return newPromise;

};

MyPromise.prototype.catch

  1. 作用:捕獲異常

  2. 返回 MyPromise


MyPromise.prototype.catch = function (onRejected) {

    return this.then(undefined, onRejected);

};

The Promise Resolution Procedure

  1. Promise解析過程,是以一個(gè) promise茂嗓、一個(gè)值 xresolve, reject做為參數(shù)的抽象過程

  2. promise 等于 x餐茵,reject 拋出異常 new TypeError('循環(huán)引用')

  3. x如果不是對(duì)象(不包括 null)或者函數(shù),執(zhí)行 resolve(x)

  4. 獲取 x.then 賦值給 then

    • then 如果是 function

      • x做為 this 調(diào)用then述吸,第一個(gè)參數(shù)是 resolvePromise忿族,第二個(gè)參數(shù)是 rejectPromise

      • resolvePromiserejectPromise只有第一次調(diào)用有效

      • resolvePromise參數(shù)為 y,執(zhí)行 resolutionPromise(promise, y, resolve, reject)

      • rejectPromise參數(shù)為 r蝌矛,執(zhí)行 reject(r)

    • then 如果不是 function

      • 執(zhí)行 resolve(x)
  5. 用捕獲上一步的異常

    • 執(zhí)行 reject(e)

    • 如果執(zhí)行過 resolvePromiserejectPromise道批,忽略


function resolutionPromise(promise, x, resolve, reject) {

    if (promise === x) {

        reject(new TypeError('循環(huán)引用'));

    }

    let then, called;

    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {

        try {

            then = x.then;

            if (typeof then === 'function') {

                then.call(x, function (y) {

                    if (called)

                        return;

                    called = true;

                    resolutionPromise(promise, y, resolve, reject);

                }, function (r) {

                    if (called)

                        return;

                    called = true;

                    reject(r);

                })

            } else {

                resolve(x);

            }

        } catch (e) {

            if (called)

                return;

            reject(e);

        }

    } else {

        resolve(x);

    }

}

MyPromise 靜態(tài)方法

MyPromise.all


MyPromise.all = function (promises) {

    let called = false;

    return new MyPromise(function (resolve, reject) {

        let newArr = [], count = 0;

        for (let i = 0; i < promises.length; i++) {

            let item = promises[i];

            if (!(item instanceof MyPromise)) {

                item = MyPromise.resolve(item);

            }

            item.then(function (data) {

                if (!called) {

                    newArr[i] = data;

                    if (i == count) {

                        resolve(newArr);

                        count++;

                    }

                }

            }, function (e) {

                if (!called) {

                    reject(e);

                    called = true;

                }

            });

        }

    });

};

MyPromise.race


MyPromise.race = function (promises) {

    return new MyPromise(function (resolve, reject) {

        let called = false;

        for (let i = 0; i < promises.length; i++) {

            let item = promises[i];

            if (!(item instanceof MyPromise)) {

                item = MyPromise.resolve(item);

            }

            item.then(function (data) {

                if (!called) {

                    resolve(data);

                    called = true;

                }

            }, function (e) {

                if (!called) {

                    reject(e);

                    called = true;

                }

            });

        }

    })

};

MyPromise.resolve


MyPromise.resolve = function (value) {

    if (value instanceof MyPromise) {

        return value;

    }

    return new MyPromise(function (resolve, reject) {

        if (typeof value !== null && typeof value === 'object' && typeof value.then === 'function') {

            value.then();

        } else {

            resolve(value);

        }

    })

};

MyPromise.reject


MyPromise.reject = function (e) {

    return new MyPromise(function (resolve, reject) {

        reject(e);

    })

};

test

  • npm i -g promises-aplus-tests

  • promises-aplus-tests Promise.js

源碼

參考資料
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市入撒,隨后出現(xiàn)的幾起案子隆豹,更是在濱河造成了極大的恐慌,老刑警劉巖衅金,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件噪伊,死亡現(xiàn)場離奇詭異,居然都是意外死亡氮唯,警方通過查閱死者的電腦和手機(jī)鉴吹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惩琉,“玉大人豆励,你說我怎么就攤上這事。” “怎么了良蒸?”我有些...
    開封第一講書人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵技扼,是天一觀的道長。 經(jīng)常有香客問我嫩痰,道長剿吻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任串纺,我火速辦了婚禮丽旅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘纺棺。我一直安慰自己榄笙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開白布祷蝌。 她就那樣靜靜地躺著茅撞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪巨朦。 梳的紋絲不亂的頭發(fā)上米丘,一...
    開封第一講書人閱讀 52,696評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音糊啡,去河邊找鬼蠕蚜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛悔橄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播腺毫,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼癣疟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了潮酒?” 一聲冷哼從身側(cè)響起睛挚,我...
    開封第一講書人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎急黎,沒想到半個(gè)月后扎狱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡勃教,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年淤击,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片故源。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡污抬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情印机,我是刑警寧澤矢腻,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站射赛,受9級(jí)特大地震影響多柑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜楣责,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一竣灌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧腐魂,春花似錦帐偎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至兔毒,卻和暖如春漫贞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背育叁。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來泰國打工迅脐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人豪嗽。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓谴蔑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親龟梦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子隐锭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361