手寫Promise最簡20行版本,實現(xiàn)異步鏈?zhǔn)秸{(diào)用。(重構(gòu)版)
前言
在面試的時候,經(jīng)常會有面試官讓你實現(xiàn)一個 Promise,如果參照 A+規(guī)范來實現(xiàn)的話,可能面到天黑都結(jié)束不了。
說到 Promise,我們首先想到的最核心的功能就是異步鏈?zhǔn)秸{(diào)用,本篇文章就帶你用 20 行代碼實現(xiàn)一個可以異步鏈?zhǔn)秸{(diào)用的 Promise。
這個 Promise 的實現(xiàn)不考慮任何異常情況,只考慮代碼最簡短,從而便于讀者理解核心的異步鏈?zhǔn)秸{(diào)用原理。
代碼
先給代碼吧,真就 20 行。
function?Promise(fn)?{
??this.cbs?=?[];
??const?resolve?=?(value)?=>?{
????setTimeout(()?=>?{
??????this.data?=?value;
??????this.cbs.forEach((cb)?=>?cb(value));
????});
??}
??fn(resolve.bind(this));
}
Promise.prototype.then?=?function?(onResolved)?{
??return?new?Promise((resolve)?=>?{
????this.cbs.push(()?=>?{
??????const?res?=?onResolved(this.data);
??????if?(res?instanceof?Promise)?{
????????res.then(resolve);
??????}?else?{
????????resolve(res);
??????}
????});
??});
};
核心案例
new?Promise((resolve)?=>?{
??setTimeout(()?=>?{
????resolve(1);
??},?500);
})
??.then((res)?=>?{
????console.log(res);
????return?new?Promise((resolve)?=>?{
??????setTimeout(()?=>?{
????????resolve(2);
??????},?500);
????});
??})
??.then(console.log);
本文將圍繞這個最核心的案例來講,這段代碼的表現(xiàn)如下:
500ms 后輸出 1 500ms 后輸出 2
實現(xiàn)
構(gòu)造函數(shù)
首先來實現(xiàn) Promise 構(gòu)造函數(shù)
function?Promise(fn)?{
??//?Promise?resolve時的回調(diào)函數(shù)集
??this.cbs?=?[];
??//?傳遞給Promise處理函數(shù)的resolve
??//?這里直接往實例上掛個data
??//?然后把onResolvedCallback數(shù)組里的函數(shù)依次執(zhí)行一遍就可以
??const?resolve?=?(value)?=>?{
????//?注意promise的then函數(shù)需要異步執(zhí)行
????setTimeout(()?=>?{
??????this.data?=?value;
??????this.cbs.forEach((cb)?=>?cb(value));
????});
??}
??//?執(zhí)行用戶傳入的函數(shù)?
??//?并且把resolve方法交給用戶執(zhí)行
??fn(resolve.bind(this));
}
好,寫到這里先回過頭來看案例
const?fn?=?(resolve)?=>?{
??setTimeout(()?=>?{
????resolve(1);
??},?500);
};
new?Promise(fn);
分開來看,fn?就是用戶傳的函數(shù),這個函數(shù)內(nèi)部調(diào)用了?resolve?函數(shù)后,就會把?promise?實例上的?cbs?全部執(zhí)行一遍。
到此為止我們還不知道?cbs?這個數(shù)組里的函數(shù)是從哪里來的,接著往下看。
then
這里是最重要的 then 實現(xiàn),鏈?zhǔn)秸{(diào)用全靠它:
Promise.prototype.then?=?function?(onResolved)?{
??//?這里叫做promise2
??return?new?Promise((resolve)?=>?{
????this.cbs.push(()?=>?{
??????const?res?=?onResolved(this.data);
??????if?(res?instanceof?Promise)?{
????????//?resolve的權(quán)力被交給了user?promise
????????res.then(resolve);
??????}?else?{
????????//?如果是普通值?就直接resolve
????????//?依次執(zhí)行cbs里的函數(shù)?并且把值傳遞給cbs
????????resolve(res);
??????}
????});
??});
};
再回到案例里
const?fn?=?(resolve)?=>?{
??setTimeout(()?=>?{
????resolve(1);
??},?500);
};
const?promise1?=?new?Promise(fn);
promise1.then((res)?=>?{
??console.log(res);
??//?user?promise
??return?new?Promise((resolve)?=>?{
????setTimeout(()?=>?{
??????resolve(2);
????},?500);
??});
});
注意這里的命名:
我們把?
new Promise?返回的實例叫做promise1在?
Promise.prototype.then?的實現(xiàn)中,我們構(gòu)造了一個新的 promise 返回,叫它promise2在用戶調(diào)用?
then?方法的時候,用戶手動構(gòu)造了一個 promise 并且返回,用來做異步的操作,叫它user promise
那么在?then?的實現(xiàn)中,內(nèi)部的 this 其實就指向promise1
而promise2的傳入的fn?函數(shù)執(zhí)行了一個?this.cbs.push(),其實是往?promise1?的cbs數(shù)組中 push 了一個函數(shù),等待后續(xù)執(zhí)行。
Promise.prototype.then?=?function?(onResolved)?{
??//?這里叫做promise2
??return?new?Promise((resolve)?=>?{
????//?這里的this其實是promise1
????this.cbs.push(()?=>?{});
??});
};
那么重點看這個 push 的函數(shù),注意,這個函數(shù)在?promise1?被 resolve 了以后才會執(zhí)行。
//?promise2
return?new?Promise((resolve)?=>?{
??this.cbs.push(()?=>?{
????//?onResolved就對應(yīng)then傳入的函數(shù)
????const?res?=?onResolved(this.data)
????//?例子中的情況?用戶自己返回了一個user?promise
????if?(res?instanceof?Promise)?{
??????//?user?promise的情況
??????//?用戶會自己決定何時resolve?promise2
??????//?只有promise2被resolve以后
??????//?then下面的鏈?zhǔn)秸{(diào)用函數(shù)才會繼續(xù)執(zhí)行
??????res.then(resolve)
????}?else?{
??????resolve(res)
????}
??})
})
如果用戶傳入給 then 的 onResolved 方法返回的是個?user promise,那么這個user promise里用戶會自己去在合適的時機?resolve promise2,那么進而這里的?res.then(resolve)?中的 resolve 就會被執(zhí)行:
if?(res?instanceof?Promise)?{
????res.then(resolve)
}
結(jié)合下面這個例子來看:
new?Promise((resolve)?=>?{
??setTimeout(()?=>?{
????//?resolve1
????resolve(1);
??},?500);
})
??//?then1
??.then((res)?=>?{
????console.log(res);
????//?user?promise
????return?new?Promise((resolve)?=>?{
??????setTimeout(()?=>?{
????????//?resolve2
????????resolve(2);
??????},?500);
????});
??})
??//?then2
??.then(console.log);
then1這一整塊其實返回的是?promise2,那么?then2?其實本質(zhì)上是?promise2.then(console.log),
也就是說?then2注冊的回調(diào)函數(shù),其實進入了promise2的?cbs?回調(diào)數(shù)組里,又因為我們剛剛知道,resolve2?調(diào)用了之后,user promise?會被 resolve,進而觸發(fā)?promise2?被 resolve,進而?promise2?里的?cbs?數(shù)組被依次觸發(fā)。
這樣就實現(xiàn)了用戶自己寫的?resolve2?執(zhí)行完畢后,then2?里的邏輯才會繼續(xù)執(zhí)行,也就是異步鏈?zhǔn)秸{(diào)用。
文章總結(jié)
本文只是簡單實現(xiàn)一個可以異步鏈?zhǔn)秸{(diào)用的 promise,而真正的 promise 比它復(fù)雜很多很多,涉及到各種異常情況、邊界情況的處理。
promise A+規(guī)范還是值得每一個合格的前端開發(fā)去閱讀的。
希望這篇文章可以對你有所幫助!
?? 看完三件事
點個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點在看,都是耍流氓 -_-)
關(guān)注我的官網(wǎng)?https://muyiy.cn,讓我們成為長期關(guān)系
關(guān)注公眾號「高級前端進階」,公眾號后臺回復(fù)「面試題」 送你高級前端面試題,回復(fù)「加群」加入面試互助交流群
