web前端面試題:你能讀懂的Promise源碼實現(xiàn)(手寫代碼)

Promise是web前端工程師在面試的過程中很難繞過的一個坎锈嫩。如果您目前處于對Promise一知半解啥箭,或僅僅是停留在可以使用的層面上彼城,建議您跟著本文敲打練習(xí)一遍恬叹,相信您一定會有所收獲!另外文章有點長……

一因俐、實現(xiàn)Promise以下功能:
const p1 = new Promise((resolve, reject) => {
    resolve("成功");
})
p1.then(value => {
    // 成功
    console.log(value);
})

const p2 = new Promise((resolve, reject) => {
    reject("失敗");
})
p2.then(undefined, reason => {
    // 失敗
    console.log(reason);
})

const p3 = new Promise((resolve, reject) => {
    throw "異常"
})
p3.then(undefined, reason => {
    // 異常
    console.log(reason);
})
1拇惋、根據(jù)上面的代碼,我們不難推斷出其基本的結(jié)構(gòu):
/*
* 創(chuàng)建一個構(gòu)造函數(shù) Promise
* 該函數(shù)接收一個 executor 執(zhí)行函數(shù)
* */
function Promise(executor) {

}
/*
* 為 Promise 函數(shù)增加 then 方法;
* then 方法接收兩個類型為 function 的參數(shù);
* 第一個參數(shù)onResolved為成功時調(diào)用的函數(shù);
* 第二個參數(shù)onRejected為失敗時調(diào)用的函數(shù);
* */
Promise.prototype.then = function (onResolved,onRejected) {

}
2抹剩、Promise 對象存在三種狀態(tài):Pending(進行中)撑帖、resolved(已成功)rejected(已失敗)澳眷。我們可以將其設(shè)置為三個常量:
// 進行中狀態(tài)
const _PENDING="pending";
// 已成功狀態(tài)
const _RESOLVED="resolved";
// 已失敗狀態(tài)
const _REJECTED="rejected";
3胡嘿、我們在實例化Promise時,resolvereject函數(shù)將會得到執(zhí)行钳踊,一旦執(zhí)行其實例的狀態(tài)會由pending更改為resolvedrejected衷敌,然后在原型對象方法then當(dāng)中進行接收。所以我們要干以下幾個事情:
  • 實例中創(chuàng)建兩個屬性statusvalue
  • 創(chuàng)建內(nèi)部函數(shù)_resolve_reject用于更新statusvalue
  • 立即執(zhí)行executor函數(shù)
代碼如下:
function Promise(executor){
    /****** 1拓瞪、實例中創(chuàng)建兩個屬性`status`與`value` ******/
    // 設(shè)置狀態(tài)初始值為 pending
    this.status = _PENDING;
    // 設(shè)置初始值為 undefined
    this.value = undefined;
   /****** 2缴罗、創(chuàng)建內(nèi)部函數(shù)`_resolve`與`_reject`用于更新`status`與`value` ******/
    // 成功時執(zhí)行
    function _resolve(value) {
        // 修改 promise 對象的狀態(tài)為 resolve
        this.status = _RESOLVED;
        // 保存成功的數(shù)據(jù)
        this.value = value;
    }
    // 失敗時執(zhí)行
    function _reject(reason) {
        // 修改 promise 對象的狀態(tài)為 resolve
        this.status = _REJECTED;
        // 保存失敗的數(shù)據(jù)
        this.value = reason;
    }
    /****** 3、立即執(zhí)行`executor`函數(shù) ******/
    try{
        // 立即執(zhí)行 executor
        executor(_resolve.bind(this),_reject.bind(this))
    }catch (err) {
        _reject.call(this,err);
    }
}
4祭埂、我們需要在then函數(shù)內(nèi)面氓,根據(jù)實例的狀態(tài)state來判斷要執(zhí)行onResolved成功函數(shù),還是onRejected失敗函數(shù)蛆橡。Promise的核心then函數(shù)初始代碼如下:
Promise.prototype.then = function (onResolved,onRejected) {
    switch (this.status) {
        // 當(dāng)狀態(tài)為resolve時舌界,執(zhí)行onResolved,并傳遞結(jié)果
        case _RESOLVED:
            onResolved(this.value);
            break
        // 當(dāng)狀態(tài)為reject時泰演,執(zhí)行onRejected呻拌,并傳遞結(jié)果
        case _REJECTED:
            onRejected(this.value);
            break
    }
}
5、當(dāng)前已完成代碼如下:
// 進行中狀態(tài)
const _PENDING="pending";
// 已成功狀態(tài)
const _RESOLVED="resolved";
// 已失敗狀態(tài)
const _REJECTED="rejected";
/*
* 創(chuàng)建一個構(gòu)造函數(shù) Promise
* 該函數(shù)接收一個 executor 執(zhí)行函數(shù)
* */
function Promise(executor){
    // 設(shè)置狀態(tài)初始值為 pending
    this.status = _PENDING;
    // 設(shè)置初始值為 undefined
    this.value = undefined;
    // 成功時執(zhí)行
    function _resolve(value) {
        // 修改 promise 對象的狀態(tài)為 resolve
        this.status = _RESOLVED;
        // 保存成功的數(shù)據(jù)
        this.value = value;
    }
    // 失敗時執(zhí)行
    function _reject(reason) {
        // 修改 promise 對象的狀態(tài)為 resolve
        this.status = _REJECTED;
        // 保存失敗的數(shù)據(jù)
        this.value = reason;
    }
    try{
        // 立即執(zhí)行 executor
        executor(_resolve.bind(this),_reject.bind(this))
    }catch (err) {
        _reject.call(this,err);
    }
}
/*
* 為 Promise 函數(shù)增加 then 方法;
* then 方法接收兩個類型為 function 的參數(shù);
* 第一個參數(shù)onResolved為成功時調(diào)用的函數(shù);
* 第二個參數(shù)onRejected為失敗時調(diào)用的函數(shù);
* */
Promise.prototype.then = function (onResolved,onRejected) {
    switch (this.status) {
        // 當(dāng)狀態(tài)為resolve時睦焕,執(zhí)行onResolved藐握,并傳遞結(jié)果
        case _RESOLVED:
            onResolved(this.value);
            break
        // 當(dāng)狀態(tài)為reject時,執(zhí)行onRejected垃喊,并傳遞結(jié)果
        case _REJECTED:
            onRejected(this.value);
            break
    }
}
二猾普、Promise的狀態(tài)state只允許更改一次

Promise即承諾,一旦承諾便會給予結(jié)果缔御,且結(jié)果是不允許更改的。也就是說狀態(tài)state一旦確定便不可更改妇蛀。

const p1 = new Promise((resolve, reject) => {
    resolve("成功");
    reject("失敗");
})
p1.then(value => {
    // 不會執(zhí)行
    console.log(value);
},reason=>{
    // 輸出:失敗
    console.log(reason);
})

以上代碼正確的輸出應(yīng)該是成功耕突,而我們自己封裝的Promise輸出結(jié)果卻為失敗笤成!說明我們的statereject("失敗")進行了二次更改,原因很簡單:我們沒有對當(dāng)前的狀態(tài)進行判斷眷茁。
所以我們要對_resolve_reject進行調(diào)整炕泳,分別在其函數(shù)體內(nèi)增加對當(dāng)前狀態(tài)的判斷,如果不是初始狀態(tài)pending則不會繼續(xù)更新狀態(tài)及數(shù)據(jù)上祈。
在構(gòu)造函數(shù)Promise中找到_resolve_reject函數(shù)培遵,代碼調(diào)整如下:

// 成功時執(zhí)行
function _resolve(value) {
    // 如果為pending退出函數(shù)
    if (this.status !== "pending")
        return;
    // 修改 promise 對象的狀態(tài)為 resolve
    this.status = _RESOLVED;
    // 保存成功的數(shù)據(jù)
    this.value = value;
}
// 失敗時執(zhí)行
function _reject(reason) {
    // 如果為pending退出函數(shù)
    if (this.status !== "pending")
        return;
    // 修改 promise 對象的狀態(tài)為 resolve
    this.status = _REJECTED;
    // 保存失敗的數(shù)據(jù)
    this.value = reason;
}

接下來,再來執(zhí)行最初的程序登刺,輸出為成功籽腕,說明狀態(tài)只允許被更新一次了!

三纸俭、then函數(shù)是異步的

因為then函數(shù)是異步的皇耗,所以在正常情況下,以下代碼的輸出應(yīng)該為:1 2 3揍很。但是采用我們自己封裝的Promise,其結(jié)果卻為1 3 2郎楼。原因:我們并未對then函數(shù)進行異步的處理。

const p = new Promise((resolve, reject) => {
    console.log(1);
    resolve("成功");
})
p.then(value=>{
    console.log(3);
    console.log()
})
console.log(2);

接下來窒悔,進入到then函數(shù)中呜袁。我們只需要將執(zhí)行回調(diào)的代碼用setTimeout進行包裹即可:

Promise.prototype.then = function (onResolved, onRejected) {
    switch (this.status) {
        // 當(dāng)狀態(tài)為resolve時,執(zhí)行onResolved简珠,并傳遞結(jié)果
        case _RESOLVED:
            // 通過 setTimeout 讓代碼異步執(zhí)行
            setTimeout(() => {
                onResolved(this.value);
            })
            break
        // 當(dāng)狀態(tài)為reject時阶界,執(zhí)行onRejected,并傳遞結(jié)果
        case _REJECTED:
            // 通過 setTimeout 讓代碼異步執(zhí)行
            setTimeout(() => {
                onRejected(this.value);
            })
            break
    }
}

再來執(zhí)行北救,結(jié)果為1 2 3荐操。問題順利解決!

四珍策、完成Promise的連綴調(diào)用(核心)
const p = new Promise((resolve, reject) => {
    resolve("成功");
})
const result = p.then();
// 輸出status為 resolved 的 Promise 實例
console.log(result);

const p2 = new Promise((resolve, reject) => {
    reject("失敗");
})
const result2 = p2.then();
// 輸出status為 resolved 的 Promise 實例
console.log(result2);

const p3 = new Promise((resolve, reject) => {
    throw "異常"
})
const result3 = p3.then();
// 輸出status為 rejected 的 Promise 實例
console.log(result3);
1托启、正常情況下,上方代碼不管成功與失敗攘宙,then 函數(shù)的返回結(jié)果始終應(yīng)該是一個Promise實例屯耸,且其狀態(tài)均為resolved。如果出現(xiàn)異常報錯蹭劈,則返回的狀態(tài)為rejected疗绣,如下:

但是,我們目前的then函數(shù)是沒有返回值的铺韧,所以我們只能得到一個undefined多矮,并且由于我們未給予then函數(shù)相對應(yīng)的參數(shù)(類型為函數(shù)),還給我們飄紅報錯了:Uncaught TypeError: onResolved is not a function

所以接下來塔逃,我們要做三件事:1讯壶、驗證參數(shù)是否為函數(shù)。 2湾盗、讓then函數(shù)直接返回Promise 3伏蚊、更改promise的狀態(tài):異常執(zhí)行reject,其它均執(zhí)行resolve

  • 驗證參數(shù)是否為函數(shù):
 // 防止使用者不傳成功或失敗回調(diào)函數(shù)格粪,所以成功失敗回調(diào)都給了默認(rèn)回調(diào)函數(shù)
onResolved = typeof onResolved === "function" ? onResolved : value => value;
onRejected = typeof onRejected === "function" ? onRejected : error => { throw error };
  • then函數(shù)返回promise躏吊,then函數(shù)完整代碼如下:
Promise.prototype.then = function (onResolved, onRejected) {
    return new Promise((resolve, reject) => {
        // 防止使用者不傳成功或失敗回調(diào)函數(shù),所以成功失敗回調(diào)都給了默認(rèn)回調(diào)函數(shù)
        onResolved = typeof onResolved === "function" ? onResolved : value => value;
        onRejected = typeof onRejected === "function" ? onRejected : error => {
            throw error
        };
        switch (this.status) {
            // 當(dāng)狀態(tài)為resolve時帐萎,執(zhí)行onResolved比伏,并傳遞結(jié)果
            case _RESOLVED:
                // 通過 setTimeout 讓代碼異步執(zhí)行
                setTimeout(() => {
                    onResolved(this.value);
                });
                break
            // 當(dāng)狀態(tài)為reject時,執(zhí)行onRejected吓肋,并傳遞結(jié)果
            case _REJECTED:
                // 通過 setTimeout 讓代碼異步執(zhí)行
                setTimeout(() => {
                    onRejected(this.value);
                });
                break
        }
    })
}
  • 更改Promise的狀態(tài):異常執(zhí)行reject,其它均執(zhí)行resolve凳怨,并傳值。
Promise.prototype.then = function (onResolved, onRejected) {
    return new Promise((resolve, reject) => {
        // 防止使用者不傳成功或失敗回調(diào)函數(shù)是鬼,所以成功失敗回調(diào)都給了默認(rèn)回調(diào)函數(shù)
        onResolved = typeof onResolved === "function" ? onResolved : value => value;
        onRejected = typeof onRejected === "function" ? onRejected : error => {
            throw error
        };
        switch (this.status) {
            // 當(dāng)狀態(tài)為resolve時肤舞,執(zhí)行onResolved,并傳遞結(jié)果
            case _RESOLVED:
                // 通過 setTimeout 讓代碼異步執(zhí)行
                setTimeout(() => {
                    // 增加try方法均蜜,如果出現(xiàn)異常李剖,執(zhí)行reject
                    try {
                        onResolved(this.value);
                        // 無異常執(zhí)行resolve
                        resolve(this.value);
                    } catch (err) {
                        // 出現(xiàn)異常,執(zhí)行reject
                        reject(err);
                    }
                });
                break
            // 當(dāng)狀態(tài)為reject時囤耳,執(zhí)行onRejected篙顺,并傳遞結(jié)果
            case _REJECTED:
                // 通過 setTimeout 讓代碼異步執(zhí)行
                setTimeout(() => {
                    // 增加try方法,如果出現(xiàn)異常充择,執(zhí)行reject
                    try {
                        onRejected(this.value);
                        // 無異常執(zhí)行resolve
                        resolve(this.value);
                    } catch (err) {
                        // 出現(xiàn)異常穴墅,執(zhí)行reject
                        reject(err);
                    }
                });
                break
        }
    })
}

接下來边锁,通過咱們封裝的程序提岔,可以得到準(zhǔn)確的數(shù)據(jù)了:


2匪傍、我們知道then在其回調(diào)函數(shù)中返回非Promise的數(shù)據(jù),最終得到的result是一個為resolved狀態(tài)的Promise(成功的狀態(tài))观挎,倘若返回的是一個Promise數(shù)據(jù)琴儿,那么最終得到的便是該Promise的狀態(tài)(成功或失敗的狀態(tài)),例如:
const p = new Promise((resolve, reject) => {
    resolve("成功");
})
const result = p.then(value=>{
    return "返回字符串"+value;
});
console.log("result",result);

const result2 = p.then(value=>{
    return new Promise((resolve,reject)=>{
        resolve("成功的Promise")
    })
});
console.log("result2",result2);

const result3 = p.then(value=>{
    return new Promise((resolve,reject)=>{
        reject("失敗的Promise")
    })
});
console.log("result3",result3);

應(yīng)該是這樣的結(jié)果:


但是嘁捷,通過我們自己封裝的Promise得到的結(jié)果都是一樣的:

  • 原因:沒有在then函數(shù)中判斷onResolvedonRejected返回類型造成。
  • 解決:判斷onResolvedonRejected的返回結(jié)果是否為Promise,如果是Promise雄嚣,則將其狀態(tài)與then要返回的Promise狀態(tài)設(shè)為一致晒屎。
Promise.prototype.then = function (onResolved, onRejected) {
    return new Promise((resolve, reject) => {
        // 防止使用者不傳成功或失敗回調(diào)函數(shù),所以成功失敗回調(diào)都給了默認(rèn)回調(diào)函數(shù)
        onResolved = typeof onResolved === "function" ? onResolved : value => value;
        onRejected = typeof onRejected === "function" ? onRejected : error => {
            throw error
        };
        switch (this.status) {
            // 當(dāng)狀態(tài)為resolve時,執(zhí)行onResolved鼓鲁,并傳遞結(jié)果
            case _RESOLVED:
                // 通過 setTimeout 讓代碼異步執(zhí)行
                setTimeout(() => {
                    // 增加try方法履肃,如果出現(xiàn)異常,執(zhí)行reject
                    try {
                        let result = onResolved(this.value);
                        // 判斷返回結(jié)果是否為Promise類型
                        if(result instanceof Promise){
                            // result.then(v => {
                            //     // 成功,修改返回的 Promise 狀態(tài)為成功
                            //     resolve(v);
                            // }, r => {
                            //     // 失敗,修改返回的 Promise 狀態(tài)為失敗
                            //     reject(r);
                            // });

                            // result 是 promise坐桩,下面這行代碼是上方代碼的簡寫形式
                            result.then(resolve,reject);
                        }else{
                            // 非Promise類型,將結(jié)果直接傳遞過去
                            resolve(result);
                        }
                    } catch (err) {
                        // 出現(xiàn)異常封锉,執(zhí)行reject
                        reject(err);
                    }
                });
                break
            // 當(dāng)狀態(tài)為reject時绵跷,執(zhí)行onRejected,并傳遞結(jié)果
            case _REJECTED:
                // 通過 setTimeout 讓代碼異步執(zhí)行
                setTimeout(() => {
                    // 增加try方法成福,如果出現(xiàn)異常碾局,執(zhí)行reject
                    try {
                        let result = onRejected(this.value);
                        // 判斷返回結(jié)果是否為Promise類型
                        if(result instanceof Promise){
                            // result.then(v => {
                            //     // 成功,修改返回的 Promise 狀態(tài)為成功
                            //     resolve(v);
                            // }, r => {
                            //     // 失敗,修改返回的 Promise 狀態(tài)為失敗
                            //     reject(r);
                            // });

                            // result 是 promise,下面這行代碼是上方代碼的簡寫形式
                            result.then(resolve,reject);
                        }else{
                            // 非Promise類型奴艾,將結(jié)果直接傳遞過去
                            resolve(result);
                        }
                    } catch (err) {
                        // 出現(xiàn)異常净当,執(zhí)行reject
                        reject(err);
                    }
                });
                break
        }
    })
}

結(jié)果:


效果雖然出來了,但是這樣的代碼確實有些臃腫蕴潦。所以我們要對其進行優(yōu)化:不難發(fā)現(xiàn)兩個case內(nèi)的代碼除了回調(diào)函數(shù)不同像啼,其它都是一樣的,所以我們可以將其進行封裝潭苞,并將回調(diào)函數(shù)作為參數(shù)傳遞忽冻。封裝優(yōu)化后:

Promise.prototype.then = function (onResolved, onRejected) {
    return new Promise((resolve, reject) => {
        /*
        * 參數(shù) cb 的值為 onResolved 或 onRejected 函數(shù)
        *  */
        function _callback(cb) {

            // 增加try方法,如果出現(xiàn)異常此疹,執(zhí)行reject
            try {
                let result = cb(this.value);
                // 判斷返回結(jié)果是否為Promise類型
                if (result instanceof Promise) {
                    // result 是 promise僧诚,下面這行代碼是上方代碼的簡寫形式
                    result.then(resolve, reject);
                } else {
                    // 非Promise類型,將結(jié)果直接傳遞過去
                    resolve(result);
                }
            } catch (err) {
                // 出現(xiàn)異常蝗碎,執(zhí)行reject
                reject(err);
            }
        }
        // 防止使用者不傳成功或失敗回調(diào)函數(shù)湖笨,所以成功失敗回調(diào)都給了默認(rèn)回調(diào)函數(shù)
        onResolved = typeof onResolved === "function" ? onResolved : value => value;
        onRejected = typeof onRejected === "function" ? onRejected : error => {
            throw error
        };
        switch (this.status) {
            // 當(dāng)狀態(tài)為resolve時,執(zhí)行onResolved蹦骑,并傳遞結(jié)果
            case _RESOLVED:
                // 通過 setTimeout 讓代碼異步執(zhí)行
                setTimeout(() => {
                    _callback.call(this, onResolved);
                });
                break;
            // 當(dāng)狀態(tài)為reject時慈省,執(zhí)行onRejected,并傳遞結(jié)果
            case _REJECTED:
                // 通過 setTimeout 讓代碼異步執(zhí)行
                setTimeout(() => {
                    _callback.call(this, onRejected);
                });
                break;
        }
    })
}
3脊串、程序?qū)懙竭@一步其實還隱藏著一個bug辫呻,如果我們采用連綴的形式會有問題,代碼如下:
let p1 = new Promise((resolve, reject) => {
    resolve('成功')
}).then(value => {
    console.log("我會輸出")
}).then(value => {
    console.log("我不會輸出")
})

正常來講琼锋,兩個then均會輸出才對放闺。而通過我們封裝的Promise,只會將第一個輸出缕坎。

  • 分析:因為我們現(xiàn)在的Promise是同步任務(wù)怖侦,所以當(dāng)我們執(zhí)行到第一個then的時候,當(dāng)前Promise的狀態(tài)已經(jīng)確定為resolved。而當(dāng)執(zhí)行到第二個then的時候匾寝,此時的Promise是通過第一個then得到的搬葬,又因為在第一個then當(dāng)中有setTimeout,使其變?yōu)榱水惒窖藁冢詴斐?code>resolve或reject不會立即調(diào)用急凰,最終導(dǎo)致在執(zhí)行第二個then時,當(dāng)前Promisestatuspending猜年。也就是說我們更改狀態(tài)后抡锈,回調(diào)方法沒有得到執(zhí)行。如果此時我們將封裝then函數(shù)當(dāng)中的setTimeout移除掉乔外,則會恢復(fù)正常床三,但將其移除掉封裝也就失去了意義。
  • 解決:我們已經(jīng)知道原因是當(dāng)Promise的狀態(tài)發(fā)生變化時杨幼,then函數(shù)的回調(diào)沒有得到調(diào)用撇簿。所以我們需要在改變狀態(tài)后調(diào)用即可〔罟海可狀態(tài)更改完成之后我們又如何才可以執(zhí)行回調(diào)四瘫?在這個時候我們可以在實例當(dāng)中創(chuàng)建一個屬性onCallBacks用于存放回調(diào)函數(shù)隊列,然后在執(zhí)行then函數(shù)時判斷當(dāng)前狀態(tài)如果為pending則說明為異步任務(wù)欲逃,只需將回調(diào)函數(shù)放置到onCallBacks 屬性中莲组。這樣當(dāng)異步修改完狀態(tài)后,我們就可以通過onCallBacks執(zhí)行回調(diào)了暖夭。代碼:
  • 在實例當(dāng)中創(chuàng)建一個屬性onCallBacks用于存放回調(diào)函數(shù)隊列锹杈。
// 添加回調(diào)函數(shù)隊列
this.onCallBacks = [];
  • then 函數(shù)中判斷當(dāng)前狀態(tài)為pending 時,將回調(diào)函數(shù)放置到 onCallBacks數(shù)組中迈着。
// 當(dāng)狀態(tài)為 pending 時竭望,將要執(zhí)行的回調(diào)函數(shù)放置到隊列中,待狀態(tài)更改完畢后再調(diào)用裕菠。
case _PENDING:
    this.onCallBacks.push({
        onResolved() {
            //獲取回調(diào)函數(shù)的執(zhí)行結(jié)果
            _callback.call(this,onResolved);
        },
        onRejected() {
            _callback.call(this,onRejected);
        }
    });
    break;
  • 當(dāng)異步修改完狀態(tài)后咬清,我們就可以通過onCallBacks 執(zhí)行回調(diào)了。
// 成功時執(zhí)行
function _resolve(value) {
    if (this.status !== "pending")
        return;

    // 修改 promise 對象的狀態(tài)為 resolve
    this.status = _RESOLVED;
    // 保存成功的數(shù)據(jù)
    this.value = value;
    //檢查回調(diào)數(shù)組中是否存在數(shù)據(jù)
    if (this.onCallBacks.length > 0) {
        // 異步執(zhí)行
        setTimeout(() => {
            this.onCallBacks.forEach(onCb => {
                onCb.onResolved.call(this);
            });
        });
    }
}
// 失敗時執(zhí)行
function _reject(reason) {
    if (this.status !== "pending")
        return;
    // 修改 promise 對象的狀態(tài)為 resolve
    this.status = _REJECTED;
    // 保存失敗的數(shù)據(jù)
    this.value = reason;
    //檢查回調(diào)數(shù)組中是否存在數(shù)據(jù)
    if (this.onCallBacks.length > 0) {
        // 異步執(zhí)行
        setTimeout(() => {
            this.onCallBacks.forEach(onCb => {
                onCb.onRejected.call(this);
            });
        });
    }
}

Promise完整代碼如下:

// 進行中狀態(tài)
const _PENDING = "pending";
// 已成功狀態(tài)
const _RESOLVED = "resolved";
// 已失敗狀態(tài)
const _REJECTED = "rejected";
/*
* 創(chuàng)建一個構(gòu)造函數(shù) Promise
* 該函數(shù)接收一個 executor 執(zhí)行函數(shù)
* */
function Promise(executor) {
    // 設(shè)置狀態(tài)初始值為 pending
    this.status = _PENDING;
    // 設(shè)置初始值為 undefined
    this.value = undefined;
    // 添加回調(diào)函數(shù)隊列
    this.onCallBacks = [];
    // 成功時執(zhí)行
    function _resolve(value) {
        if (this.status !== "pending")
            return;

        // 修改 promise 對象的狀態(tài)為 resolve
        this.status = _RESOLVED;
        // 保存成功的數(shù)據(jù)
        this.value = value;
        //檢查回調(diào)數(shù)組中是否存在數(shù)據(jù)
        if (this.onCallBacks.length > 0) {
            // 異步執(zhí)行
            setTimeout(() => {
                this.onCallBacks.forEach(onCb => {
                    onCb.onResolved.call(this);
                });
            });
        }
    }
    // 失敗時執(zhí)行
    function _reject(reason) {
        if (this.status !== "pending")
            return;
        // 修改 promise 對象的狀態(tài)為 resolve
        this.status = _REJECTED;
        // 保存失敗的數(shù)據(jù)
        this.value = reason;
        //檢查回調(diào)數(shù)組中是否存在數(shù)據(jù)
        if (this.onCallBacks.length > 0) {
            // 異步執(zhí)行
            setTimeout(() => {
                this.onCallBacks.forEach(onCb => {
                    onCb.onRejected.call(this);
                });
            });
        }
    }
    try {
        // 立即執(zhí)行 executor
        executor(_resolve.bind(this), _reject.bind(this))
    } catch (err) {
        _reject.call(this, err);
    }
}

/*
* 為 Promise 函數(shù)增加 then 方法;
* then 方法接收兩個類型為 function 的參數(shù);
* 第一個參數(shù) onResolved 為成功時調(diào)用的函數(shù);
* 第二個參數(shù) onRejected 為失敗時調(diào)用的函數(shù);
* */
Promise.prototype.then = function (onResolved, onRejected) {
    return new Promise((resolve, reject) => {
        /*
        * 參數(shù) cb 的值為 onResolved 或 onRejected 函數(shù)
        *  */
        function _callback(cb) {
            // 增加try方法奴潘,如果出現(xiàn)異常旧烧,執(zhí)行reject
            try {
                let result = cb(this.value);
                // 判斷返回結(jié)果是否為Promise類型
                if (result instanceof Promise) {
                    // result 是 promise,下面這行代碼是上方代碼的簡寫形式
                    result.then(resolve, reject);
                } else {
                    // 非Promise類型画髓,將結(jié)果直接傳遞過去
                    resolve(result);
                }
            } catch (err) {
                // 出現(xiàn)異常掘剪,執(zhí)行reject
                reject(err);
            }
        }

        // 防止使用者不傳成功或失敗回調(diào)函數(shù),所以成功失敗回調(diào)都給了默認(rèn)回調(diào)函數(shù)
        onResolved = typeof onResolved === "function" ? onResolved : value => value;
        onRejected = typeof onRejected === "function" ? onRejected : error => {
            throw error
        };
        switch (this.status) {
            // 當(dāng)狀態(tài)為resolve時奈虾,執(zhí)行onResolved夺谁,并傳遞結(jié)果
            case _RESOLVED:
                // 通過 setTimeout 讓代碼異步執(zhí)行
                setTimeout(() => {
                    _callback.call(this, onResolved);
                });
                break;
            // 當(dāng)狀態(tài)為reject時廉赔,執(zhí)行onRejected,并傳遞結(jié)果
            case _REJECTED:
                // 通過 setTimeout 讓代碼異步執(zhí)行
                setTimeout(() => {
                    _callback.call(this, onRejected);
                });
                break;
            // 當(dāng)狀態(tài)為 pending 時匾鸥,將要執(zhí)行的回調(diào)函數(shù)放置到隊列中蜡塌,待狀態(tài)更改完畢后再調(diào)用。
            case _PENDING:
                this.onCallBacks.push({
                    onResolved() {
                        //獲取回調(diào)函數(shù)的執(zhí)行結(jié)果
                        _callback.call(this,onResolved);
                    },
                    onRejected() {
                        _callback.call(this,onRejected);
                    }
                });
                break;
        }
    })
}

截止到目前勿负,我們已經(jīng)完成了Promise的鏈?zhǔn)秸{(diào)用馏艾。

五、其它API
// catch方法的封裝
Promise.prototype.catch = function (onRejected) {
    return this.then(undefined, onRejected);
}
// 函數(shù)對象 resolve 的封裝
Promise.resolve = function (value) {
    return new Promise((resolve, reject) => {
        if (value instanceof Promise) {
            value.then(resolve, reject);
        } else {
            resolve(value);
        }
    });
}
// 函數(shù)對象 reject 的封裝
Promise.reject = function (reason) {
    return new Promise((resolve, reject) => {
        reject(reason);
    })
}
//函數(shù)對象 all 的封裝
Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let pValues = [];
        let flag = 0;
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(v => {
                pValues[i] = v;
                flag++;
                if (flag >= promises.length) {
                    resolve(pValues);
                }
            }, r => {
                reject(r);
            })
        }
    });
}
// 函數(shù)對象 race
Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        for(let i=0;i<promises.length;i++){
            promises[i].then(value=>{
                resolve(value);
            }, reason=>{
                reject(reason);
            })
        }
    });
}

—————END—————
喜歡本文的朋友們奴愉,歡迎關(guān)注公眾號 張培躍攒至,收看更多精彩內(nèi)容!躁劣!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市库菲,隨后出現(xiàn)的幾起案子账忘,更是在濱河造成了極大的恐慌,老刑警劉巖熙宇,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鳖擒,死亡現(xiàn)場離奇詭異,居然都是意外死亡烫止,警方通過查閱死者的電腦和手機蒋荚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來馆蠕,“玉大人期升,你說我怎么就攤上這事』ス” “怎么了播赁?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吼渡。 經(jīng)常有香客問我容为,道長,這世上最難降的妖魔是什么寺酪? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任坎背,我火速辦了婚禮,結(jié)果婚禮上寄雀,老公的妹妹穿的比我還像新娘得滤。我一直安慰自己,他們只是感情好盒犹,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布耿戚。 她就那樣靜靜地躺著湿故,像睡著了一般。 火紅的嫁衣襯著肌膚如雪膜蛔。 梳的紋絲不亂的頭發(fā)上坛猪,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機與錄音皂股,去河邊找鬼墅茉。 笑死,一個胖子當(dāng)著我的面吹牛呜呐,可吹牛的內(nèi)容都是我干的就斤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蘑辑,長吁一口氣:“原來是場噩夢啊……” “哼洋机!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起洋魂,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绷旗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后副砍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體衔肢,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年豁翎,在試婚紗的時候發(fā)現(xiàn)自己被綠了角骤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡心剥,死狀恐怖邦尊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情优烧,我是刑警寧澤胳赌,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站匙隔,受9級特大地震影響疑苫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纷责,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一捍掺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧再膳,春花似錦挺勿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽禾嫉。三九已至,卻和暖如春蚊丐,著一層夾襖步出監(jiān)牢的瞬間熙参,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工麦备, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留孽椰,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓凛篙,卻偏偏與公主長得像黍匾,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子呛梆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359