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
時,resolve
或reject
函數(shù)將會得到執(zhí)行钳踊,一旦執(zhí)行其實例的狀態(tài)會由pending
更改為resolved
或rejected
衷敌,然后在原型對象方法then
當(dāng)中進行接收。所以我們要干以下幾個事情:
- 實例中創(chuàng)建兩個屬性
status
與value
- 創(chuàng)建內(nèi)部函數(shù)
_resolve
與_reject
用于更新status
與value
- 立即執(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é)果卻為失敗
笤成!說明我們的state
被reject("失敗")
進行了二次更改,原因很簡單:我們沒有對當(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ù)中判斷onResolved
與onRejected
返回類型造成。 - 解決:判斷
onResolved
與onRejected
的返回結(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)前Promise
的status
為pending
猜年。也就是說我們更改狀態(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)容!躁劣!