Promise 鏈?zhǔn)秸{(diào)用原理精簡(jiǎn)示例
前言
在面試的過(guò)程中,總有一些面試官會(huì)問(wèn)你,手寫(xiě)一個(gè)簡(jiǎn)易版的Promise
得行不,得行的話就寫(xiě)一個(gè)出來(lái)看看,啪一哈,就把紙和筆給了你。 我們思索半天就寫(xiě)出來(lái)了個(gè)下面這個(gè)。 哦豁,高薪張開(kāi)了它的翅膀,遠(yuǎn)離了我們。
class Promise { constructor (resolve, reject) {} resolve () {} reject (){} then () {} catch () {} once () {} all () {} ... }
本篇文章將不講述手寫(xiě)出來(lái)一個(gè)簡(jiǎn)易的Promise
,感興趣的朋友可以去看我這篇文章 -> Promise詳解-手寫(xiě)Promise,實(shí)現(xiàn)一款自己的簡(jiǎn)易Promise
本篇文章記錄的是如何實(shí)現(xiàn)Promise
的核心功能之一的.then 鏈?zhǔn)秸{(diào)用,采用構(gòu)造函數(shù)的寫(xiě)法,本篇文章的代碼不考慮任何容錯(cuò)和異常處理,只單獨(dú)說(shuō)明其鏈?zhǔn)秸{(diào)用原理,方便理解。
先擺上完整代碼,去掉注釋和一些換行共20行有余。
代碼
function CustomPromise (fn) { // 回調(diào)收集 this.callbackList = [] // 傳遞給Promise處理函數(shù)的resolve const resolve = (value) => { // 注意promise的then函數(shù)需要異步執(zhí)行 setTimeout(() => { // 這里直接往實(shí)例上掛個(gè)data this.data = value; // 把callbackList數(shù)組里的函數(shù)依次執(zhí)行一遍 this.callbackList.forEach(cb => cb(value)) }); } /* 執(zhí)行用戶傳入的函數(shù) 并且把resolve方法交給用戶執(zhí)行 */ fn(resolve) } /* 重點(diǎn) */ // 往構(gòu)造函數(shù)的原型上掛載.then方法 CustomPromise.prototype.then = function (onReaolved) { // return 一個(gè)promise 實(shí)例 return new CustomPromise((resolve) => { // 往回調(diào)數(shù)組中插入回調(diào) this.callbackList.push(()=>{ const response = onReaolved(this.data) // 判斷是否是一個(gè) CustomPromise if(response instanceof CustomPromise){ // resolve 的權(quán)力被交給了user promise response.then(resolve) }else{ // 如果是普通值,直接resolve // 依次執(zhí)行callbackList里的函數(shù) 并且把值傳遞給callbackList resolve(response) } }) }) }
經(jīng)典案例
new CustomPromise((resolve) => { setTimeout(() => { // resolve1 resolve(1); }, 300); }).then((res) => {// then1 console.log(res); // 返回一個(gè) CustomPromise return new CustomPromise((resolve) => { setTimeout(() => { // resolve2 resolve(2); }, 300); }); }).then(res => {// then2 console.log(res); });
完整的代碼和例子已奉上,現(xiàn)在來(lái)進(jìn)行解釋。 固然結(jié)果很重要,但過(guò)程也很重要。我們要做到 知其然知其所以然。
解析
第一步
首先,我們我們先創(chuàng)建這樣一個(gè)Promise
, 這里需要使用匿名函數(shù),不能使用箭頭函數(shù),或者你可以根據(jù)這個(gè)方法已class
類的方法進(jìn)行實(shí)現(xiàn)。
大概步驟如下:
- 聲明構(gòu)造函數(shù)/類
- 在內(nèi)部聲明一個(gè)數(shù)組名為
callbackList
用來(lái)裝回調(diào),并放到this
里面 - 聲明一個(gè)名
resolve
的方法,用來(lái)傳遞給Promise
進(jìn)行處理,注意:resolve 內(nèi)部需要為異步,這里可以采用 setTimeout 實(shí)現(xiàn) - 循環(huán)
callbackList
并執(zhí)行里面的方法
寫(xiě)出來(lái)后的樣子長(zhǎng)這樣:
function CustomPromise (fn) { // 回調(diào)收集 this.callbackList = [] // 傳遞給Promise處理函數(shù)的resolve const resolve = (value) => { // 注意promise的then函數(shù)需要異步執(zhí)行 setTimeout(() => { // 這里直接往實(shí)例上掛個(gè)data this.data = value; // 把callbackList數(shù)組里的函數(shù)依次執(zhí)行一遍 this.callbackList.forEach(cb => cb(value)) }); } /* - fn 為用戶傳進(jìn)來(lái)的函數(shù) - 執(zhí)行用戶傳入的函數(shù) - 并且把resolve方法交給用戶執(zhí)行 */ fn(resolve) }
第二步
注意:第二步是本篇文章的重點(diǎn),也是這個(gè)核心功能的一個(gè)重點(diǎn)。
我們需要往CustomPromise
的原型上掛載一個(gè).then
的方法。并返回的是一個(gè)Promise
實(shí)例,這里依舊使用的是匿名函數(shù)。
完整代碼長(zhǎng)這樣:
// 往構(gòu)造函數(shù)的原型上掛載.then方法 CustomPromise.prototype.then = function (onReaolved) { // return 一個(gè)promise 實(shí)例 return new CustomPromise((resolve) => { // 往回調(diào)數(shù)組中插入回調(diào) this.callbackList.push(()=>{ const response = onReaolved(this.data) // 判斷是否是一個(gè) CustomPromise if(response instanceof CustomPromise){ // resolve 的權(quán)力被交給了user promise response.then(resolve) }else{ // 如果是普通值,直接resolve // 依次執(zhí)行callbackList里的函數(shù) 并且把值傳遞給callbackList resolve(response) } }) }) }
寫(xiě)出來(lái)過(guò)后,在結(jié)合上面的那個(gè)例子使用,不能說(shuō)和原生Promise
一模一樣,但使用起來(lái)的鏈?zhǔn)叫Ч麉s是一毛一樣。
分析說(shuō)明,此過(guò)程需結(jié)合上文中的案例一起閱讀
const promise1 = new CustomPromise((resolve) => { setTimeout(() => resolve(1)); }) promise1.then((res) => { const userPromise = new CustomPromise((resolve) => { setTimeout(() => resolve(2), 300); }); return userPromise });
說(shuō)明:
- 我們把
new Promise
返回的實(shí)例叫做promise1
- 在
Promise.prototype.then
的實(shí)現(xiàn)中,我們構(gòu)造了一個(gè)新的promise
返回,叫它promise2
在調(diào)用then
方法的時(shí)候,用戶手動(dòng)構(gòu)造了一個(gè)promise
并且返回,用來(lái)做異步的操作,叫它userPromise
,那么在then
的實(shí)現(xiàn)中,內(nèi)部的this
其實(shí)就指向promise1
而promise2
的傳入的fn
函數(shù)執(zhí)行了一個(gè)this.cbs.push()
的操作,其實(shí)是往promise1
的callbackList
數(shù)組中push
了一個(gè)函數(shù),等待后續(xù)執(zhí)行
CustomPromise.prototype.then = function (onReaolved) { // promise 2 return new CustomPromise((resolve) => { // 往回調(diào)數(shù)組中插入回調(diào) this.callbackList.push(()=>{}) }) }
如果用戶傳入給then
的onResolved
方法返回的是個(gè)userPromise
,那么這個(gè)userPromise
里用戶會(huì)自己去在合適的時(shí)機(jī) resolvePromise2
,那么進(jìn)而這里的response.then(resolve)
中的resolve
就會(huì)被執(zhí)行
if(response instanceof CustomPromise){ response.then(resolve) }
再結(jié)合上面的經(jīng)典案例看,我這里再放一遍
new CustomPromise((resolve) => { setTimeout(() => { // resolve1 resolve(1); }, 300); }).then((res) => {// then1 console.log(res); // userPromise return new CustomPromise((resolve) => { setTimeout(() => { // resolve2 resolve(2); }, 300); }); }).then(res => {// then2 console.log(res); });
then1
這一整塊其實(shí)返回的是promise2
,那么then2
其實(shí)本質(zhì)上是promise2.then(()=>{})
, 也就是說(shuō)then2
注冊(cè)的回調(diào)函數(shù),其實(shí)進(jìn)入了promise2
的callbackList
回調(diào)數(shù)組里。 又因?yàn)槲覀儎倓傊溃?code>resolve2調(diào)用了之后,userPromise
會(huì)被resolve
,進(jìn)而觸發(fā)promise2
被resolve
,進(jìn)而 promise2
里的callbackList
數(shù)組被依次觸發(fā)。 這樣就實(shí)現(xiàn)了用戶自己寫(xiě)的resolve2
執(zhí)行完畢后,then2
里的邏輯才會(huì)繼續(xù)執(zhí)行,也就是異步鏈?zhǔn)秸{(diào)用。
說(shuō)句題外話,這個(gè)有點(diǎn)繞,當(dāng)時(shí)還是看了好一會(huì)才看懂。
好了,當(dāng)你看到這里的時(shí)候,這篇文章已經(jīng)接近尾聲了,是時(shí)候進(jìn)行總結(jié)了。
總結(jié)
本篇文章只是根據(jù)其原理實(shí)現(xiàn)的一個(gè)簡(jiǎn)易鏈?zhǔn)秸{(diào)用的過(guò)程,真正的Promise
并沒(méi)有這么簡(jiǎn)單,和上文中的比起來(lái)復(fù)雜很多,而且涉及到很多的異常、容錯(cuò)、邊界等情況的處理。
最后推薦一下Promise A+規(guī)范
-> 點(diǎn)我查看規(guī)范,很值得去看,相信看完后會(huì)對(duì)Promise
有一個(gè)更深的了解。
以上就是Promise 鏈?zhǔn)秸{(diào)用原理精簡(jiǎn)示例的詳細(xì)內(nèi)容,更多關(guān)于Promise 鏈?zhǔn)秸{(diào)用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript實(shí)現(xiàn)點(diǎn)擊單元格改變背景色的方法
這篇文章主要介紹了JavaScript實(shí)現(xiàn)點(diǎn)擊單元格改變背景色的方法,涉及JavaScript響應(yīng)鼠標(biāo)事件動(dòng)態(tài)操作頁(yè)面元素屬性的相關(guān)技巧,需要的朋友可以參考下2016-02-02純JavaScript實(shí)現(xiàn)的兼容各瀏覽器的添加和移除事件封裝
這篇文章主要介紹了純JavaScript實(shí)現(xiàn)的兼容各瀏覽器的添加和移除事件封裝,本文直接給出實(shí)現(xiàn)代碼,代碼中帶詳細(xì)注釋,需要的朋友可以參考下2015-03-03javascript實(shí)現(xiàn)檢驗(yàn)的各種規(guī)則
這篇文章主要介紹了javascript實(shí)現(xiàn)檢驗(yàn)的各種規(guī)則,涉及javascript針對(duì)手機(jī)號(hào)、郵箱、網(wǎng)址、漢字及圖片等相關(guān)檢測(cè)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07[Bootstrap-插件使用]Jcrop+fileinput組合實(shí)現(xiàn)頭像上傳功能實(shí)例代碼
這篇文章主要介紹了[Bootstrap-插件使用]Jcrop+fileinput組合實(shí)現(xiàn)頭像上傳功能實(shí)例代碼,非常具有實(shí)用價(jià)值,需要的朋友可以參考下。2016-12-12用JavaScript實(shí)現(xiàn)仿Windows關(guān)機(jī)效果
用JavaScript實(shí)現(xiàn)仿Windows關(guān)機(jī)效果...2007-03-03前端開(kāi)發(fā)不得不知的10個(gè)最佳ES6特性
ES6已經(jīng)不再是JavaScript最新的標(biāo)準(zhǔn),但是它已經(jīng)廣泛用于編程實(shí)踐中。下面通過(guò)本文給大家分享前端開(kāi)發(fā)不得不知的10個(gè)最佳ES6特性,感興趣的朋友參考下吧2017-08-08js window對(duì)象屬性和方法相關(guān)資料整理
這篇文章主要介紹了js window對(duì)象屬性和方法相關(guān)資料整理,需要的朋友可以參考下2015-11-11JavaScript實(shí)現(xiàn)頁(yè)面定時(shí)刷新(定時(shí)器,meta)
很多朋友看到定時(shí),很容易想到用js定時(shí)器,還有盆友用meta來(lái)設(shè)置,下面小編給大家介紹js實(shí)現(xiàn)頁(yè)面定時(shí)刷新的方法,一起看看吧2016-10-10