實(shí)現(xiàn)一個(gè)符合 Promise/A+規(guī)范的 Promise(typescript 版)
(給前端大學(xué)加星標(biāo),提升前端技能.)
轉(zhuǎn)自:Col0ring
juejin.cn/post/6886360224308035598
寫(xiě)在前面
沒(méi)錯(cuò),這又是一篇關(guān)于手寫(xiě) Promise 的文章,想必大家已經(jīng)看過(guò)很多相關(guān) Promise 的文章,關(guān)于一些 Promise 出現(xiàn)原因等問(wèn)題我就不詳細(xì)說(shuō)了,網(wǎng)上有很多資料。這次我們使用 typescript,從 ES6 中的 Promise 類(lèi)型定義入手,分析 Promise 及相關(guān)方法的傳入?yún)?shù)和返回值,手寫(xiě)一個(gè) typescript 版本的 Promise。
Promise/A+ 規(guī)范
Promise/A+ 規(guī)范是業(yè)內(nèi)所有的 Promise 類(lèi)庫(kù)的統(tǒng)一規(guī)范,我們要寫(xiě)的 Promise 也要符合這一規(guī)范(英文文檔請(qǐng)查看 Promises/A+:https://promisesaplus.com/,相關(guān)中文翻譯 Promise A+ 規(guī)范:http://malcolmyu.github.io/malnote/2015/06/12/Promises-A-Plus/,感謝譯者)。
由于內(nèi)容過(guò)多,在下面的編碼中會(huì)一一進(jìn)行實(shí)現(xiàn),這里先提出幾個(gè)術(shù)語(yǔ):
解決(fulfill):指一個(gè) promise 成功時(shí)進(jìn)行的一系列操作,如狀態(tài)的改變、回調(diào)的執(zhí)行。雖然規(guī)范中用 fulfill來(lái)表示解決,但在后世的 promise 實(shí)現(xiàn)多以resolve來(lái)指代之。拒絕(reject):指一個(gè) promise 失敗時(shí)進(jìn)行的一系列操作。 終值(eventual value):所謂終值,指的是 promise 被解決(fulfill)時(shí)傳遞給解決回調(diào)的值,由于 promise 有一次性的特征,因此當(dāng)這個(gè)值被傳遞時(shí),標(biāo)志著 promise 等待態(tài)的結(jié)束,故稱(chēng)之終值,有時(shí)也直接簡(jiǎn)稱(chēng)為值(value)。 據(jù)因(reason):也就是拒絕原因,指在 promise 被拒絕(reject) 時(shí)傳遞給拒絕回調(diào)的值。
值得注意的是,核心的 Promises/A+ 規(guī)范不設(shè)計(jì)如何創(chuàng)建、解決和拒絕 promise,而是專(zhuān)注于提供一個(gè)通用的 then 方法。所以,完成了對(duì)于then方法的交互,其實(shí)也就基本完成了對(duì)于 Promises/A+ 規(guī)范的實(shí)現(xiàn)。
實(shí)現(xiàn) Promise
基本功能
首先我們要實(shí)現(xiàn) Promise 的基本功能,傳入生成器,reslove和reject函數(shù)的執(zhí)行以及調(diào)用then函數(shù)對(duì)于值的基本獲取,先來(lái)看看構(gòu)造函數(shù)的類(lèi)型定義:
//?這是類(lèi)本身,還有?all,?race?等方法在這里面定義
interface?PromiseConstructor?{
????/**
?????*?A?reference?to?the?prototype.
?????*/
????readonly?prototype:?Promise<any>;
????/**
?????*?Creates?a?new?Promise.
?????*?@param?executor?A?callback?used?to?initialize?the?promise.?This?callback?is?passed?two?arguments:
?????*?a?resolve?callback?used?to?resolve?the?promise?with?a?value?or?the?result?of?another?promise,
?????*?and?a?reject?callback?used?to?reject?the?promise?with?a?provided?reason?or?error.
?????*/
???//?這里就是重點(diǎn)部分了
????new?(executor:?(resolve:?(value?:?T?|?PromiseLike )?=>?void,?reject:?(reason?:?any)?=>?void)?=>?void):?Promise<T>;
??//?...
}
上面我們會(huì)看到兩個(gè)其余的接口類(lèi)型,分別是Promise和PromiseLike,其中Promise就是實(shí)例對(duì)象的相關(guān)屬性接口:
//?這時(shí)實(shí)例對(duì)象,下面只是?ES2015?的接口屬性,因?yàn)楹罄m(xù)?Promise?做過(guò)更新,后續(xù)會(huì)說(shuō)明更多實(shí)例屬性
interface?Promise?{
????/**
?????*?Attaches?callbacks?for?the?resolution?and/or?rejection?of?the?Promise.
?????*?@param?onfulfilled?The?callback?to?execute?when?the?Promise?is?resolved.
?????*?@param?onrejected?The?callback?to?execute?when?the?Promise?is?rejected.
?????*?@returns?A?Promise?for?the?completion?of?which?ever?callback?is?executed.
?????*/
????then(onfulfilled?:?((value:?T)?=>?TResult1?|?PromiseLike )?|?undefined?|?null,?onrejected?:?((reason:?any)?=>?TResult2?|?PromiseLike )?|?undefined?|?null):?Promise<TResult1?|?TResult2>;
????//...
}
PromiseLike的接口在下面:
interface?PromiseLike?{
????/**
?????*?Attaches?callbacks?for?the?resolution?and/or?rejection?of?the?Promise.
?????*?@param?onfulfilled?The?callback?to?execute?when?the?Promise?is?resolved.
?????*?@param?onrejected?The?callback?to?execute?when?the?Promise?is?rejected.
?????*?@returns?A?Promise?for?the?completion?of?which?ever?callback?is?executed.
?????*/
????then(onfulfilled?:?((value:?T)?=>?TResult1?|?PromiseLike )?|?undefined?|?null,?onrejected?:?((reason:?any)?=>?TResult2?|?PromiseLike )?|?undefined?|?null):?PromiseLike<TResult1?|?TResult2>;
}
可以看出PromiseLike接口定義的就是一個(gè)擁有then()方法的對(duì)象(官方的叫法是 thenable),只要有then()方法就會(huì)將其當(dāng)做一個(gè)Promise實(shí)例看待。
//?試驗(yàn)一下
new?Promise((resolve)?=>?{
??resolve({
????prop:?'common?property',
????//?這里我們自己構(gòu)造了個(gè)?then?方法,Promise?會(huì)自動(dòng)為?then?方法?reslove?和?reject?函數(shù)
????then(reslove2:?any)?{
??????reslove2('promiselike')
????}
??})
}).then((res)?=>?{
??//?果然,被當(dāng)做成了?Promise
??console.log(res)?//?promiselike
})
需要注意的是,Promise 內(nèi)部的回調(diào)函數(shù)的異步執(zhí)行機(jī)制是使用的微任務(wù),而我們所使用的環(huán)境中并沒(méi)有為我們提供微任務(wù)的相關(guān) api,所以代碼中都是使用setTimeout進(jìn)行異步模擬,將回調(diào)直接推入到事件環(huán)的最后。
如果對(duì)事件環(huán)與微任務(wù)不太了解,可以查看下這篇文章 徹底搞懂 JS 事件輪詢(xún):https://juejin.im/post/6844904198581010439。
下面是代碼實(shí)現(xiàn):
//?創(chuàng)建一枚舉類(lèi)型保存響應(yīng)狀態(tài)的變量
enum?Status?{
??PENDING?=?'pending',
??FULFILLED?=?'fulfilled',
??REJECTED?=?'rejected'
}
//?將需要類(lèi)型提出來(lái)
type?Resolve?=?(value:?T?|?PromiseLike )?=>?void
type?Reject?=?(reason?:?any)?=>?void
type?Executor?=?(resolve:?Resolve,?reject:?Reject )?=>?void
type?onFulfilled?=
??|?((value:?T)?=>?TResult1?|?PromiseLike )
??|?undefined
??|?null
type?onRejected<TResult2>?=
??|?((reason:?any)?=>?TResult2?|?PromiseLike )
??|?undefined
??|?null
/*?
?將判斷是否為?thenable?單獨(dú)提出來(lái),減少代碼冗余,不然每次都需要使用:
?((typeof?value?===?'object'?&&?value?!==?null)?||
??????typeof?value?===?'function')?&&?typeof?(value?as?PromiseLike ).then?===?'function'
??來(lái)進(jìn)行判斷,同時(shí)也有更好的?typescript?提示
*/
function?isPromise(value:?any):?value?is?PromiseLike<any>?{
??return?(
????((typeof?value?===?'object'?&&?value?!==?null)?||
??????typeof?value?===?'function')?&&
????typeof?value.then?===?'function'
??)
}
class?MyPromise<T>?{
??//?剛開(kāi)始的狀態(tài)
??status:?Status?=?Status.PENDING
??//?保存當(dāng)前?Promise?的終值,這里讓它一定會(huì)有值
??private?value!:?T
??//?保存當(dāng)前?Promise?的據(jù)因
??private?reason?:?any
??private?onFulfilledCallback:?(()?=>?void)[]?=?[]?//成功的回調(diào)
??private?onRejectedCallback:?(()?=>?void)[]?=?[]?//失敗的回調(diào)
??constructor(executor:?Executor )?{
????try?{
??????//?防止?this?丟失
??????executor(this._resolve.bind(this),?this._reject.bind(this))
????}?catch?(e)?{
??????//?出錯(cuò)直接?reject
??????this._reject(e)
????}
??}
??private?_resolve(value:?T?|?PromiseLike )?{
????try{
??????//?模擬微任務(wù)異步
??????setTimeout(()?=>?{
????????//?判斷是否是個(gè)?thenable?對(duì)象,如果是,我們直接取?pending?結(jié)束后的值
????????if?(isPromise(value))?{
??????????//?再次將內(nèi)部的?resolve?和?reject?函數(shù)傳入
??????????value.then(this._resolve.bind(this),?this._reject.bind(this))
??????????return
????????}
????????//?如果是?pending?狀態(tài)就變?yōu)?fulfilled
????????if?(this.status?===?Status.PENDING)?{
??????????this.status?=?Status.FULFILLED
??????????//?這里的?value?類(lèi)型只會(huì)是?T
??????????this.value?=?value
??????????//?resolve?后執(zhí)行?.then?時(shí)傳入的回調(diào)
??????????this.onFulfilledCallback.forEach((fn)?=>?fn())
????????}
??????})
????}catch(err){
??????//?捕獲如果傳入的是?Promise?時(shí)在內(nèi)部拋出錯(cuò)誤后的捕獲
??????this._reject(err)
????}
??}
??//?內(nèi)部的?reject?函數(shù),就是我們實(shí)例?Promise?傳入給用戶(hù)調(diào)用的?reject
??private?_reject(reason:?any)?{
????//?大體用法同上,這里不用進(jìn)行值穿透,所以不用判斷是否為?Promise?對(duì)象了
????setTimeout(()?=>?{
??????if?(this.status?===?Status.PENDING)?{
????????this.status?=?Status.REJECTED
????????this.reason?=?reason
????????this.onRejectedCallback.forEach((fn)?=>?fn())
??????}
????})
??}
??public?then<TResult1?=?T,?TResult2?=?never>(
????onfulfilled?:?onFulfilled,
????onrejected?:?onRejected
?? ):?MyPromise<TResult1?|?TResult2>?{
????//??關(guān)于?onfulfilled?與?onrejected?如果沒(méi)有傳我們需要進(jìn)行值的透?jìng)?,但是在基本功能的?shí)現(xiàn)中我們先不管這個(gè)問(wèn)題,默認(rèn)一定會(huì)傳入函數(shù)
????//?判斷當(dāng)前狀態(tài),如果是異步?reslove?或?reject,那么此時(shí)的?status?還是?pending
????if?(this.status?===?Status.FULFILLED)?{
??????setTimeout(()?=>?{
????????onfulfilled!(this.value)
??????})
????}
????if?(this.status?===?Status.REJECTED)?{
??????setTimeout(()?=>?{
????????onrejected!(this.reason)
??????})
????}
????if?(this.status?===?Status.PENDING)?{
??????//?如果為?pending,需要將?onFulfilled?和?onRejected?函數(shù)都存放起來(lái),狀態(tài)確定后再依次執(zhí)行
??????//?執(zhí)行回調(diào)的時(shí)候有?setTimeout,這里就不加了
??????this.onFulfilledCallback.push(()?=>?{
????????onfulfilled!(this.value)
??????})
??????this.onRejectedCallback.push(()?=>?{
????????onrejected!(this.reason)
??????})
????}
????//?鏈?zhǔn)秸{(diào)用,這段代碼現(xiàn)在可以直接無(wú)視,為了不讓?ts?類(lèi)型報(bào)錯(cuò)加的,因?yàn)?.then?返回一個(gè)的?Promise?的值是依賴(lài)上一個(gè)?Promise?的狀態(tài)和結(jié)果的
????return?new?MyPromise(()?=>?{})
??}
}
OK,上面已經(jīng)完成了一個(gè)只有一條鏈的 Promise,下面做一下測(cè)試:
//?同步
new?MyPromise((reslove,?reject)?=>?{
??reslove('success')
}).then(
??(res)?=>?{
????console.log(res)?//?success
??},
??(err)?=>?{
????console.log(err)
??}
)
//?異步
new?MyPromise((reslove,?reject)?=>?{
??setTimeout(()?=>?{
????reslove('timeout?success')
??},?2000)
}).then(
??(res)?=>?{
????console.log(res)?//?timeout?success
??},
??(err)?=>?{
????console.log(err)
??}
)
結(jié)果為立刻打印success,兩秒后打印timeout success,符合我們的預(yù)期。
then 的深入(重點(diǎn))
就如前面所說(shuō),Promise/A+ 規(guī)范的整個(gè)核心都在于對(duì)于then方法的處理。并且還有第三方測(cè)試庫(kù) promises-aplus-tests:https://github.com/promises-aplus/promises-tests 測(cè)試我們所寫(xiě)的 Promise 是否符合規(guī)范,我們后面也會(huì)用使用這個(gè)測(cè)試庫(kù)進(jìn)行測(cè)試。
鏈?zhǔn)秸{(diào)用
要實(shí)現(xiàn)then的鏈?zhǔn)秸{(diào)用,需要返回一個(gè)新的 Promise,同時(shí)不管在then中回調(diào)函數(shù)onfulfilled和onrejected返回了什么值,都可以在這個(gè)新的 Promise 的then方法的回調(diào)函數(shù)參數(shù)中得到。
我們用x來(lái)作為then方法中傳入的onfulfilled或onrejected的返回值,用promise來(lái)表示then方法返回的那個(gè)新的 Promise,依據(jù) Promise/A+ 規(guī)范,我們應(yīng)該對(duì)這段解決過(guò)程 [[Resolve]](promise, x) 做如下操作:
x與promise相等:如果promise和x指向同一對(duì)象,以TypeError為據(jù)因拒絕執(zhí)行promise。x為 Promise:如果x為 Promise ,則使promise接受x的狀態(tài)。
如果 x處于等待態(tài),promise需保持為等待態(tài)直至x被執(zhí)行或拒絕如果 x處于執(zhí)行態(tài),用相同的值執(zhí)行promise如果 x處于拒絕態(tài),用相同的據(jù)因拒絕promisex為對(duì)象或函數(shù)
如果 x為對(duì)象或者函數(shù):1. ?把x.then賦值給then2. ?如果取x.then的值時(shí)拋出錯(cuò)誤e,則以e為據(jù)因拒絕promise3. ?如果then是函數(shù),將x作為函數(shù)的作用域this調(diào)用之。傳遞兩個(gè)回調(diào)函數(shù)作為參數(shù),第一個(gè)參數(shù)叫做resolvePromise,第二個(gè)參數(shù)叫做rejectPromise
如果 resolvePromise以值y為參數(shù)被調(diào)用,則運(yùn)行[[Resolve]](promise, y)(就是繼續(xù)遞歸這段解決過(guò)程)如果 rejectPromise以據(jù)因r為參數(shù)被調(diào)用,則以據(jù)因r拒絕promise如果 resolvePromise和rejectPromise均被調(diào)用,或者被同一參數(shù)調(diào)用了多次,則優(yōu)先采用首次調(diào)用并忽略剩下的調(diào)用
如果調(diào)用 then方法拋出了異常e:
如果 resolvePromise或rejectPromise已經(jīng)被調(diào)用,則忽略之否則以 e為據(jù)因拒絕promise
4. ?如果then不是函數(shù),以x為參數(shù)執(zhí)行promise如果 x不為對(duì)象或者函數(shù),以x為參數(shù)執(zhí)行promise
大體的流程圖:

雖然上面看著有點(diǎn)多,其實(shí)大體來(lái)說(shuō)就是讓我們符合以下規(guī)則:
如果 then 中的回調(diào)函數(shù)返回一個(gè)值(非 Promise 實(shí)例或是 thenable 對(duì)象)或沒(méi)有返回值(也就是返回 undefined),那么 then 返回的 Promise 將會(huì)成為接受狀態(tài),并且將返回的值作為接受狀態(tài)的回調(diào)函數(shù)(
onfulfilled)的參數(shù)值。new?Promise<void>((reslove)?=>?{
reslove()
})
.then(()?=>?{
??return?'success'
})
.then((res)?=>?{
??console.log(res)?//?success
})
復(fù)制代碼如果 then 中的回調(diào)函數(shù)拋出一個(gè)錯(cuò)誤,那么 then 返回的 Promise 將會(huì)成為拒絕狀態(tài),并且將拋出的錯(cuò)誤作為拒絕狀態(tài)的回調(diào)函數(shù)(
onrejected)的參數(shù)值。new?Promise<void>((reslove)?=>?{
reslove()
})
.then(()?=>?{
??throw?new?Error('error?message')
})
.then(
??()?=>?{},
??(err)?=>?{
????console.log(err)?//?Error:?error?message
??}
)循環(huán)返回 Promise 拋出錯(cuò)誤就像下面這樣 :
const?promise2:?Promise<any>?=?new?Promise<void>((reslove)?=>?{
reslove()
}).then(()?=>?{
return?promise2
})
promise2.then(()?=>?{},?console.log)?//?[TypeError:?Chaining?cycle?detected?for?promise?#<Promise>]如果 then 中的回調(diào)函數(shù)返回一個(gè)已經(jīng)是接受狀態(tài)(fulfilled)的 Promise(我們這里暫且叫做
promise1),那么 then 返回的 Promise 也會(huì)成為接受狀態(tài),并且將promise1中then的回調(diào)函數(shù)的參數(shù)值,作為該被返回的 Promise 的接受狀態(tài)回調(diào)函數(shù)的參數(shù)值。new?Promise<void>((reslove)?=>?{
reslove()
})
.then(()?=>?{
??return?Promise.reslove('success')
})
.then((res)?=>?{
??console.log(res)?//?success
})如果 then 中的回調(diào)函數(shù)返回一個(gè)已經(jīng)是拒絕狀態(tài)(rejected)的 Promise(這里我們暫且叫做
promise2),那么 then 返回的 Promise 也會(huì)成為拒絕狀態(tài),并且將promise2中then的回調(diào)函數(shù)的參數(shù)值,作為該被返回的 Promise 的拒絕狀態(tài)回調(diào)函數(shù)的參數(shù)值。new?Promise<void>((reslove)?=>?{
reslove()
})
.then(()?=>?{
???return?new?Promise.reject('error?message')
})
.then(
??()?=>?{},
??(err)?=>?{
????console.log(err)?//?error?message
??}
)如果 then 中的回調(diào)函數(shù)返回一個(gè)未定狀態(tài)(pending)的 Promise(這里我們暫且叫做
promise3),那么 then 返回 Promise 的狀態(tài)也是未定的,并且它的終態(tài)與promise3的終態(tài)相同。同時(shí)它變?yōu)榻K態(tài)時(shí)調(diào)用then的回調(diào)函數(shù)參數(shù)與promise3變?yōu)榻K態(tài)時(shí)的回調(diào)函數(shù)的參數(shù)是相同的。new?Promise<void>((reslove)?=>?{
reslove()
})
.then(()?=>?{
???return?new?Promise((reslove,?reject)?=>?{
?????setTimeout(()=>{
??????reslove('delay')
??????},?2000)
??})
})
.then(
??res?=>?{
????console.log(res)?//?兩秒后打印?delay
??}
)
好了,照著上面的解決過(guò)程 ,我們來(lái)寫(xiě)一下這個(gè)處理函數(shù):
function?resolvePromise<T>(
??promise2:?MyPromise,
??x:?T?|?PromiseLike,
??resolve:?Resolve,
??reject:?Reject
)?{
??//?不能引用同一個(gè)對(duì)象,不然會(huì)無(wú)限循環(huán)的
??if?(promise2?===?x)?{
????const?e?=?new?TypeError(
??????'TypeError:?Chaining?cycle?detected?for?promise?#'
????)
????//?清空棧信息,不太清楚為什么?Promise?要清除這個(gè),先不管了,繼續(xù)往下
????e.stack?=?''
????//?直接進(jìn)入錯(cuò)誤的回調(diào)
????return?reject(e)
??}
??let?called?=?false?//?防止多次調(diào)用
??//?如果?x?為?Promise,通過(guò)上面的知識(shí)我們知道判斷是否是個(gè)?Promise?或者像?Promise?我們是判斷一個(gè)對(duì)象是否有?then?方法,可以發(fā)現(xiàn)在下面判斷是否是對(duì)象或者函數(shù)中也有相同的判斷,所以這里我們可以直接省略
??//?如果?x?是對(duì)象或函數(shù)
??if?((typeof?x?===?'object'?&&?x?!=?null)?||?typeof?x?===?'function')?{
????try?{
??????/*?
??????存儲(chǔ)了一個(gè)指向 x.then 的引用,然后測(cè)試并調(diào)用該引用,以避免多次訪問(wèn) x.then 屬性。這種預(yù)防措施確保了該屬性的一致性,因?yàn)槠渲悼赡茉跈z索調(diào)用時(shí)被改變。
??????注:這里可以用我們封裝的判斷方法 isPromise 判斷,但是既然跟著解決過(guò)程走,那么還是老老實(shí)實(shí)操作一下吧
??????*/
??????//?手動(dòng)轉(zhuǎn)一下類(lèi)型
??????const?then?=?(x?as?PromiseLike).then
??????if?(typeof?then?===?'function')?{
????????//?這里其實(shí)就是調(diào)用傳入的?Promise?的?then?方法,下面代碼就是執(zhí)行了?x.then(()=>{},()=>{})
????????then.call(
??????????x,
??????????(y)?=>?{
????????????if?(called)?return
????????????called?=?true
????????????//?如果是?Promise,我們應(yīng)該遞歸地獲取到最終狀態(tài)的值,傳入相同的處理函數(shù),不論是成功還是失敗都能直接拋出到最外層
????????????resolvePromise(promise2,?y,?resolve,?reject)
??????????},
??????????(r)?=>?{
????????????if?(called)?return
????????????called?=?true
????????????//?如果傳入的?Promise?被拒絕,直接拋出到最外層
????????????reject(r)
??????????}
????????)
??????}?else?{
????????//?不是?Promise?對(duì)象,當(dāng)做普通值處理
????????resolve(x)
??????}
????}?catch?(e)?{
??????//?如果中間有錯(cuò)誤。直接變?yōu)榫芙^態(tài)
??????//?但是如果出現(xiàn)錯(cuò)誤之前已經(jīng)改變了狀態(tài),那么久不用管
??????if?(called)?return
??????called?=?true
??????reject(e)
????}
??}?else?{
????//?普通值處理
????resolve(x)
??}
}
可以將上面的處理過(guò)程的代碼實(shí)現(xiàn)與描述一步步帶入查看,基本上都是能吻合的。
下面將其帶入到then中:
class?MyPromise?{
??//?...
??public?then(
????onfulfilled?:?onFulfilled,
????onrejected?:?onRejected
??):?MyPromise?{
??????
????//?現(xiàn)在我們將這個(gè)新生成的?Promise?和現(xiàn)在的?Promise?相互聯(lián)系
????const?promise2?=?new?MyPromise((resolve,?reject)?=>?{
??????if?(this.status?===?Status.FULFILLED)?{
????????setTimeout(()?=>?{
??????????try?{
????????????//??獲取到?x,然后與要返回的?Promise?產(chǎn)生聯(lián)系
????????????let?x?=?onfulfilled!(this.value)
????????????resolvePromise(promise2,?x,?resolve,?reject)
??????????}?catch?(e)?{
????????????reject(e)
??????????}
????????})
??????}
??????if?(this.status?===?Status.REJECTED)?{
????????setTimeout(()?=>?{
??????????try?{
????????????//??獲取到?x,然后與要返回的?Promise?產(chǎn)生聯(lián)系
????????????let?x?=?onrejected!(this.reason)
????????????resolvePromise(promise2,?x,?resolve,?reject)
??????????}?catch?(e)?{
????????????reject(e)
??????????}
????????})
??????}
??????if?(this.status?===?Status.PENDING)?{
????????//?如果為?pending,需要將?onFulfilled?和?onRejected?函數(shù)都存放起來(lái),狀態(tài)確定后再依次執(zhí)行
????????//?執(zhí)行回調(diào)的時(shí)候有?setTimeout,這里就不加了
????????this.onFulfilledCallback.push(()?=>?{
??????????try?{
????????????let?x?=?onfulfilled!(this.value)
????????????resolvePromise(promise2,?x,?resolve,?reject)
??????????}?catch?(e)?{
????????????reject(e)
??????????}
????????})
????????this.onRejectedCallback.push(()?=>?{
??????????try?{
????????????let?x?=?onrejected!(this.reason)
????????????resolvePromise(promise2,?x,?resolve,?reject)
??????????}?catch?(e)?{
????????????reject(e)
??????????}
????????})
??????}
????})
????return?promise2
??}
??//...
}
測(cè)試一下:
new?MyPromise<void>((resolve)?=>?{
??resolve()
})
??.then(()?=>?{
????return?'step1'
??})
??.then((res)?=>?{
????return?res?+?':'?+?'step2'
??})
??.then((res)?=>?{
????console.log(res)?//?step1:step2
??})
很好,完美符合預(yù)期。
值的穿透
之前我們使用then時(shí)都是假設(shè)我們一定會(huì)向then中傳入回調(diào)函數(shù),但是事實(shí)上在 Promise/A+ 規(guī)范中兩個(gè)回調(diào)函數(shù)都是可以缺省的,這也是為什么我會(huì)在后面加上?。當(dāng)我們不向其傳入回調(diào)函數(shù)時(shí),此時(shí)就會(huì)觸發(fā)值的穿透效果。
//?就像下面這樣
new?Promise((reslove)?=>?{
??reslove('hello')
})
??.then()
??.then()
??.then()
??.then((res)?=>?{
????console.log(res)?//?'hello'
??})
所以我們需要改造一下我們的then函數(shù),改造方法其實(shí)非常簡(jiǎn)單:
class?MyPromise?{
??//?...
??public?then(
????onfulfilled?:?onFulfilled,
????onrejected?:?onRejected
??):?MyPromise?{
??//?如果傳入的不是函數(shù),就進(jìn)行值的穿透,成功回調(diào)是返回相同的值,失敗的回調(diào)是直接拋出錯(cuò)誤
????//?注意這里不能直接給上面?zhèn)魅氲膮?shù)添加默認(rèn)值,因?yàn)樾枰袛嗍欠袷呛瘮?shù)
????const?onfulfilledFn?=
??????typeof?onfulfilled?===?'function'
??????????onfulfilled
????????:?(v:?T?|?TResult1)?=>?v?as?TResult1
????const?onrejectedFn?=
??????typeof?onrejected?===?'function'
??????????onrejected
????????:?(e:?any)?=>?{
????????????throw?e
??????????}
???//?將下面的?onfulfilled?改成?onfulfilledFn,onrejected?改成?onrejectedFn?就行了
????//?現(xiàn)在我們將這個(gè)新生成的?Promise?和現(xiàn)在的?Promise?相互聯(lián)系
????const?promise2?=?new?MyPromise((resolve,?reject)?=>?{
??????if?(this.status?===?Status.FULFILLED)?{
????????setTimeout(()?=>?{
??????????try?{
????????????//??獲取到?x,然后與要返回的?Promise?產(chǎn)生聯(lián)系
????????????let?x?=?onfulfilledFn(this.value)
????????????resolvePromise(promise2,?x,?resolve,?reject)
??????????}?catch?(e)?{
????????????reject(e)
??????????}
????????})
??????}
??????if?(this.status?===?Status.REJECTED)?{
????????setTimeout(()?=>?{
??????????try?{
????????????//??獲取到?x,然后與要返回的?Promise?產(chǎn)生聯(lián)系
????????????let?x?=?onrejectedFn(this.reason)
????????????resolvePromise(promise2,?x,?resolve,?reject)
??????????}?catch?(e)?{
????????????reject(e)
??????????}
????????})
??????}
??????if?(this.status?===?Status.PENDING)?{
????????//?如果為?pending,需要將?onFulfilled?和?onRejected?函數(shù)都存放起來(lái),狀態(tài)確定后再依次執(zhí)行
????????//?執(zhí)行回調(diào)的時(shí)候有?setTimeout,這里就不加了
????????this.onFulfilledCallback.push(()?=>?{
??????????try?{
????????????let?x?=?onfulfilledFn(this.value)
????????????resolvePromise(promise2,?x,?resolve,?reject)
??????????}?catch?(e)?{
????????????reject(e)
??????????}
????????})
????????this.onRejectedCallback.push(()?=>?{
??????????try?{
????????????let?x?=?onrejectedFn(this.reason)
????????????resolvePromise(promise2,?x,?resolve,?reject)
??????????}?catch?(e)?{
????????????reject(e)
??????????}
????????})
??????}
????})
????return?promise2
??}
??//...
}
好了,看到了這里,恭喜你,已經(jīng)完成了 Promise 最重要的部分,后續(xù)的所有 api 基本都是圍繞著上面所寫(xiě)的部分展開(kāi)的。
規(guī)范測(cè)試
使用我們前面提到的測(cè)試 Promise 規(guī)范的第三方庫(kù)來(lái)進(jìn)行測(cè)試:
npm?install?promises-aplus-tests?-D
#?or
yarn?add?promises-aplus-tests?-D
復(fù)制代碼
//?在文件末尾加上
//?忽略?typescript?校驗(yàn)
//?@ts-ignore
MyPromise.defer?=?MyPromise.deferred?=?function?()?{
??let?dfd:?any?=?{}
??dfd.promise?=?new?MyPromise((resolve,?reject)?=>?{
????dfd.resolve?=?resolve
????dfd.reject?=?reject
??})
??return?dfd
}
export?=?MyPromise
然后使用tsc命令將ts文件編譯,運(yùn)行npx promises-aplus-tests 編譯后的js文件位置。

遺憾的是,我們的 Promise 并沒(méi)有通過(guò)所有的測(cè)試,但是我們可以看到這 16 個(gè)未通過(guò)測(cè)試的報(bào)錯(cuò)都相同,調(diào)用處理的回調(diào)超時(shí)(超過(guò)了 200 ms 的延時(shí)),我的個(gè)人理解是由于setTimeout這個(gè) api 不能完全模擬微任務(wù)而造成的延時(shí)效果(說(shuō)錯(cuò)了請(qǐng)大佬們輕點(diǎn)噴)。
如果要單看測(cè)試結(jié)果來(lái)說(shuō),想全部通過(guò)測(cè)試我們可以把最開(kāi)始定義的 Promise 內(nèi)部的reslove函數(shù)做一個(gè)修改:
class?MyPromise{
??private?_resolve(value:?T?|?PromiseLike)?{
????try{
??????setTimeout(()?=>?{
????????/*
?????????刪除下面這段代碼就可通過(guò)全部測(cè)試:
??????????if?(isPromise(value))?{
????????????value.then(this._resolve.bind(this),?this._reject.bind(this))
????????????return
??????????}
????????*/
????????if?(this.status?===?Status.PENDING)?{
??????????this.status?=?Status.FULFILLED
??????????this.value?=?value?as?T?//?強(qiáng)制轉(zhuǎn)換類(lèi)型
??????????this.onFulfilledCallback.forEach((fn)?=>?fn())
????????}
??????})
????}catch(err){
??????this._reject(err)
????}
??}
}
當(dāng)然,雖然通過(guò)了全部測(cè)試,但是很明顯并不是符合預(yù)期的結(jié)果,當(dāng)我們使用一個(gè)PromiseLike對(duì)象時(shí)與真實(shí)的 Promise 結(jié)果并不一致:
//?還是用最開(kāi)始的那個(gè)例子
new?Promise((resolve)?=>?{
??resolve({
????prop:?'common?property',
????then(reslove2:?any)?{
??????reslove2('promiselike')
????}
??})
}).then((res)?=>?{
??//?真實(shí)的?Promise?這里是?promiselike
??console.log(res)?//?{?prop:?'common?property',?then:?[Function:?then]?}?
})
所以,就結(jié)果而言,也算是基本合格吧。
Promise 的拓展方法
在前面我們已經(jīng)完成了 Promise 的核心部分,現(xiàn)在可以依靠之前的代碼再次完善我們的 Promise。
注: 在 Promise 的接口定義里,所有的實(shí)例方法都是定義在Promise接口中的,所有的靜態(tài)方法都是定義在PromiseConstructor接口中的。
Promise.prototype.catch
Promise.prototype.catch(onrejected) 方法返回一個(gè)Promise,并且處理拒絕的情況。它的行為與調(diào)用Promise.prototype.then(undefined, onRejected)相同。
接口類(lèi)型:
interface?Promise?{
????/**
?????*?Attaches?a?callback?for?only?the?rejection?of?the?Promise.
?????*?@param?onrejected?The?callback?to?execute?when?the?Promise?is?rejected.
?????*?@returns?A?Promise?for?the?completion?of?the?callback.
?????*/
????catch(onrejected?:?((reason:?any)?=>?TResult?|?PromiseLike )?|?undefined?|?null):?Promise<T?|?TResult>;
}
實(shí)現(xiàn)代碼:
interface?MyPromise?{
??//?事實(shí)上它內(nèi)部就是這樣調(diào)用的
??public?catch(
????onrejected?:?onRejected
??):?MyPromise?{
????return?this.then(null,?onrejected)
??}
}
Promise.resolve
Promise.resolve(value) 方法返回一個(gè)以給定值解析后的 Promise 對(duì)象。
如果這個(gè)值是一個(gè) Promise 實(shí)例 ,那么將返回這個(gè) Promise 實(shí)例。 如果這個(gè)值是 thenable,會(huì)一直跟隨thenable拿到它的最終狀態(tài)。如果都不是上面的值,返回的 Promise 實(shí)例將用這個(gè)值作為成功狀態(tài)的終值。
接口類(lèi)型:
interface?PromiseConstructor?{
???//?可以看到有兩種函數(shù)體,所以我們需要進(jìn)行函數(shù)重載的定義
?????/**
?????*?Creates?a?new?resolved?promise.
?????*?@returns?A?resolved?promise.
?????*/
????resolve():?Promise<void>;
????/**
?????*?Creates?a?new?resolved?promise?for?the?provided?value.
?????*?@param?value?A?promise.
?????*?@returns?A?promise?whose?internal?state?matches?the?provided?promise.
?????*/
????resolve(value:?T?|?PromiseLike):?Promise;
}
實(shí)現(xiàn)代碼:
interface?MyPromise?{
??//?函數(shù)重載
??static?resolve():?MyPromise<void>
??static?resolve(value:?T?|?PromiseLike):?MyPromise
??//?最后的函數(shù)實(shí)體需要同時(shí)支持上面兩種函數(shù)重載的類(lèi)型,所以我們變成可選值
??static?resolve(value?:?T?|?PromiseLike):?MyPromise?{
????//?如果是?Promise,直接返回當(dāng)前?Promise
????if?(value?instanceof?MyPromise)?{
??????return?value
????}
????return?new?MyPromise((resolve)?=>?{
??????//?我們?cè)趦?nèi)部已經(jīng)做了對(duì)?thenable?的處理了,所以直接?reslove
??????//?因?yàn)楸仨殏髦?,所以這里就強(qiáng)制推斷了
??????resolve(value!)
????})
??}
}
Promise.reject
Promise.reject(reason) 方法返回一個(gè)帶有拒絕原因的Promise對(duì)象。
接口類(lèi)型:
interface?PromiseConstructor?{
????/**
?????*?Creates?a?new?rejected?promise?for?the?provided?reason.
?????*?@param?reason?The?reason?the?promise?was?rejected.
?????*?@returns?A?new?rejected?Promise.
?????*/
????reject(reason?:?any):?Promise;
}
實(shí)現(xiàn)代碼:
interface?MyPromise?{
??static?reject(reason?:?any):?MyPromise?{
???//?不需要額外判斷
????return?new?MyPromise((resolve,?reject)?=>?{
??????reject(reason)
????})
??}
}
Promise.all
Promise.all(iterable)方法接收一個(gè)iterable對(duì)象,返回一個(gè) Promise 實(shí)例,此實(shí)例在 iterable 參數(shù)內(nèi)所有的 thenable 狀態(tài)都為fulfilled或參數(shù)中不包含 thenable 時(shí)狀態(tài)為fulfilled,并且reslove一個(gè)包含了所有傳入thenable的reslove值的數(shù)組。如果參數(shù)內(nèi)的 thenable 有一個(gè)狀態(tài)為rejected,此實(shí)例狀態(tài)也為rejected,并且reject第一個(gè)失敗 thenable 的結(jié)果。否者如果有thenable的狀態(tài)為pending,此實(shí)例的狀態(tài)也為pending。
接口定義:
interface?PromiseConstructor?{
??????/**
?????*?Creates?a?Promise?that?is?resolved?with?an?array?of?results?when?all?of?the?provided?Promises
?????*?resolve,?or?rejected?when?any?Promise?is?rejected.
?????*?@param?values?An?array?of?Promises.
?????*?@returns?A?new?Promise.
?????*/
????all(values:?readonly?[T1?|?PromiseLike,?T2?|?PromiseLike,?T3?|?PromiseLike,?T4?|?PromiseLike,?T5?|?PromiseLike,?T6?|?PromiseLike,?T7?|?PromiseLike,?T8?|?PromiseLike,?T9?|?PromiseLike,?T10?|?PromiseLike]):?Promise<[T1,?T2,?T3,?T4,?T5,?T6,?T7,?T8,?T9,?T10]>;
??
????all(values:?readonly?[T1?|?PromiseLike,?T2?|?PromiseLike,?T3?|?PromiseLike,?T4?|?PromiseLike,?T5?|?PromiseLike,?T6?|?PromiseLike,?T7?|?PromiseLike,?T8?|?PromiseLike,?T9?|?PromiseLike]):?Promise<[T1,?T2,?T3,?T4,?T5,?T6,?T7,?T8,?T9]>;
??
????all(values:?readonly?[T1?|?PromiseLike,?T2?|?PromiseLike,?T3?|?PromiseLike,?T4?|?PromiseLike,?T5?|?PromiseLike,?T6?|?PromiseLike,?T7?|?PromiseLike,?T8?|?PromiseLike]):?Promise<[T1,?T2,?T3,?T4,?T5,?T6,?T7,?T8]>;
??
????all(values:?readonly?[T1?|?PromiseLike,?T2?|?PromiseLike,?T3?|?PromiseLike,?T4?|?PromiseLike,?T5?|?PromiseLike,?T6?|?PromiseLike,?T7?|?PromiseLike]):?Promise<[T1,?T2,?T3,?T4,?T5,?T6,?T7]>;
??
????all(values:?readonly?[T1?|?PromiseLike,?T2?|?PromiseLike,?T3?|?PromiseLike,?T4?|?PromiseLike,?T5?|?PromiseLike,?T6?|?PromiseLike]):?Promise<[T1,?T2,?T3,?T4,?T5,?T6]>;
??
????all(values:?readonly?[T1?|?PromiseLike,?T2?|?PromiseLike,?T3?|?PromiseLike,?T4?|?PromiseLike,?T5?|?PromiseLike]):?Promise<[T1,?T2,?T3,?T4,?T5]>;
????all(values:?readonly?[T1?|?PromiseLike,?T2?|?PromiseLike,?T3?|?PromiseLike,?T4?|?PromiseLike]):?Promise<[T1,?T2,?T3,?T4]>;
??
????all(values:?readonly?[T1?|?PromiseLike,?T2?|?PromiseLike,?T3?|?PromiseLike]):?Promise<[T1,?T2,?T3]>;
??
????all(values:?readonly?[T1?|?PromiseLike,?T2?|?PromiseLike]):?Promise<[T1,?T2]>;
????all(values:?readonly?(T?|?PromiseLike)[]):?Promise;
????//?看著有點(diǎn)多,其實(shí)上面都是表示傳入?yún)?shù)是一個(gè)數(shù)組的情況,這樣寫(xiě)是因?yàn)閭魅氲?Promise?中的?T?可能不同而重載不同元組類(lèi)型
????
????//?see:?lib.es2015.iterable.d.ts
????all(values:?Iterable>):?Promise;
}
在類(lèi)型定義中我們可以看到,當(dāng)我們傳入給Promise.all()一個(gè)迭代器(Iterable)的時(shí)候參數(shù)也是正確的,而數(shù)組本質(zhì)也是一個(gè)迭代器,所以我們的編碼操作可以完全圍繞著迭代器進(jìn)行展開(kāi)。
實(shí)現(xiàn)代碼:
interface?MyPromise?{
??static?all(
??values:?readonly?[
???T1?|?PromiseLike,
???T2?|?PromiseLike,
???T3?|?PromiseLike,
???T4?|?PromiseLike,
???T5?|?PromiseLike,
???T6?|?PromiseLike,
???T7?|?PromiseLike,
???T8?|?PromiseLike,
???T9?|?PromiseLike,
???T10?|?PromiseLike
???]
??):?MyPromise<[T1,?T2,?T3,?T4,?T5,?T6,?T7,?T8,?T9,?T10]>
??//?.....太占篇幅,省略了
??static?all(values:?Iterable>):?MyPromise
??//?我們這里的實(shí)際實(shí)現(xiàn)也完全按照按照迭代器來(lái)實(shí)現(xiàn)就行了
??static?all(values:?Iterable>):?MyPromise?{
????return?new?MyPromise((resolve,?reject)?=>?{
??????//?PromiseLike?對(duì)象會(huì)跟蹤轉(zhuǎn)換為?T
??????const?resultArr:?T[]?=?[]
??????//??判斷是否已經(jīng)全部完成了
??????const?doneArr:?boolean[]?=?[]
??????//?獲取迭代器對(duì)象
??????let?iter?=?values[Symbol.iterator]()
??????//?獲取值?{value:xxx,?done:?false}
??????let?cur?=?iter.next()
??????//?判斷迭代器是否迭代完畢同時(shí)將最后得到的值放入結(jié)果數(shù)組中
??????const?resolveResult?=?(value:?T,?index:?number,?done?:?boolean)?=>?{
????????resultArr[index]?=?value
????????doneArr[index]?=?true
????????if?(done?&&?doneArr.every((item)?=>?item))?{
??????????resolve(resultArr)
????????}
??????}
??????for?(let?i?=?0;?!cur.done;?i++)?{
????????const?value?=?cur.value
????????doneArr.push(false)
????????cur?=?iter.next()
????????if?(isPromise(value))?{
??????????value.then((value:?T)?=>?{
????????????resolveResult(value,?i,?cur.done)
??????????},?reject)
????????}?else?{
??????????resolveResult(value,?i,?cur.done)
????????}
??????}
????})
??}
Promise.race
Promise.race(iterable) 方法接收一個(gè)iterable對(duì)象,返回一個(gè) Promise,一旦迭代器中的某個(gè)thenable的狀態(tài)變?yōu)?code style="">fulfiled或rejected,該實(shí)例的狀態(tài)就會(huì)變成fulfiled或rejected。
接口定義:
interface?PromiseConstructor?{
????/**
?????*?Creates?a?Promise?that?is?resolved?or?rejected?when?any?of?the?provided?Promises?are?resolved
?????*?or?rejected.
?????*?@param?values?An?array?of?Promises.
?????*?@returns?A?new?Promise.
?????*/
????race(values:?readonly?T[]):?Promiseextends?PromiseLike???U?:?T>;
????//?see:?lib.es2015.iterable.d.ts
????race(values:?Iterable):?Promiseextends?PromiseLike???U?:?T>;
}
代碼實(shí)現(xiàn):
class?MyPromise?{
??static?race(
????values:?Iterable
??):?MyPromiseextends?PromiseLike???U?:?T>
??static?race(
????values:?readonly?T[]
??):?MyPromiseextends?PromiseLike???U?:?T>
??//?還是直接使用迭代器
??static?race(
????values:?Iterable
??):?MyPromiseextends?PromiseLike???U?:?T>?{
??????return?new?MyPromise((resolve,?reject)?=>?{
????????const?iter?=?values[Symbol.iterator]()
????????let?cur?=?iter.next()
????????while?(!cur.done)?{
??????????const?value?=?cur.value
??????????cur?=?iter.next()
??????????if?(isPromise(value))?{
????????????value.then(resolve,?reject)
??????????}?else?{
????????????//?普通值,這時(shí)的值為?T,但是?Typescript?無(wú)法再深度判斷了,需要自己手動(dòng)轉(zhuǎn)換
????????????resolve(value?as?T?extends?PromiseLike???U?:?T)
??????????}
????????}
??????})
??}
}
Promise.prototype.finally
ES2018提出
Promise.prototype.finally(onfinally) 方法返回一個(gè)新的 Promise,并且該 Promise 的狀態(tài)為 Promise 鏈條中前一個(gè) Promise 的狀態(tài)。在上一個(gè) Promise 結(jié)束時(shí),無(wú)論結(jié)果狀態(tài)是fulfilled或者是rejected,都會(huì)執(zhí)行指定的回調(diào)函數(shù)。 這為在Promise是否成功完成后都需要執(zhí)行的代碼提供了一種方式。避免了同樣的語(yǔ)句需要在then()和catch()中各寫(xiě)一次的情況。
具體用法:
//?不使用?finally
new?Promise((resolve)?=>?{
???resolve()
}).then(()?=>?{
??console.log('success')
??console.log('finally')
})
.catch(()?=>?{
??console.log('error')
??console.log('finally')
})
//?使用?finally
new?Promise((resolve)?=>?{
???resolve()
}).then(()?=>?{
??console.log('success')
})
.catch(()?=>?{
??console.log('error')
})
.finally(()?=>?{
????console.log('finally')
})
接口定義:
interface?Promise?{
??????/**
?????*?Attaches?a?callback?that?is?invoked?when?the?Promise?is?settled?(fulfilled?or?rejected).?The
?????*?resolved?value?cannot?be?modified?from?the?callback.
?????*?@param?onfinally?The?callback?to?execute?when?the?Promise?is?settled?(fulfilled?or?rejected).
?????*?@returns?A?Promise?for?the?completion?of?the?callback.
?????*/
????finally(onfinally?:?(()?=>?void)?|?undefined?|?null):?Promise<T>
}
代碼實(shí)現(xiàn):
class?MyPromise?{
??//?無(wú)論如何都會(huì)執(zhí)行
??public?finally(onfinally?:?onFinally):?MyPromise?{
????return?this.then(
??????(value)?=>
????????MyPromise.resolve(
?????//?如果?onfinally?返回的是一個(gè)?thenable?也會(huì)等返回的?thenable?狀態(tài)改變才會(huì)進(jìn)行后續(xù)的?Promise
??????????typeof?onfinally?===?'function'???onfinally()?:?onfinally
????????).then(()?=>?value),
??????(reason)?=>
????????MyPromise.resolve(
??????????typeof?onfinally?===?'function'???onfinally()?:?onfinally
????????).then(()?=>?{
??????????throw?reason
????????})
????)
??}
}
Promise.allSettled
ES2020提出
Promise.allSettled(iterable)方法接收一個(gè)iterable對(duì)象,返回一個(gè) Promise 實(shí)例,該實(shí)例的狀態(tài)總是fulfilled或pending。在 iterable 參數(shù)內(nèi)所有的 thenable 狀態(tài)不論為fullfilled還是rejected,reslove或reject的值都會(huì)被包裝成一個(gè)對(duì)象保留,當(dāng)所有的thenable執(zhí)行完畢后該 Promise 實(shí)例會(huì)reslove一個(gè)包含了這些所有對(duì)象的數(shù)組。如果有thenable的狀態(tài)為pending,此實(shí)例的狀態(tài)也為pending。
具體用法:
const?promise1?=?new?Promise((resolve)?=>?{
??resolve(1)
})
const?promise2?=?new?Promise((resolve)?=>?{
??resolve(2)
})
const?promise3?=?new?Promise((resolve,?reject)?=>?{
??reject(3)
})
Promise.allSettled([promise1,?promise2,?promise3]).then(console.log)
/*
打印結(jié)果為:
[
??{?status:?'fulfilled',?value:?1?},
??{?status:?'fulfilled',?value:?2?},
??{?status:?'rejected',?reason:?3?}
]
*/
接口定義:
interface?PromiseConstructor?{
??/**
?????*?Creates?a?Promise?that?is?resolved?with?an?array?of?results?when?all
?????*?of?the?provided?Promises?resolve?or?reject.
?????*?@param?values?An?array?of?Promises.
?????*?@returns?A?new?Promise.
?????*/
????allSettledextends?readonly?unknown[]?|?readonly?[unknown]>(values:?T):
????????Promise<{?-readonly?[P?in?keyof?T]:?PromiseSettledResultextends?PromiseLike???U?:?T[P]>?}>;
????/**
?????*?Creates?a?Promise?that?is?resolved?with?an?array?of?results?when?all
?????*?of?the?provided?Promises?resolve?or?reject.
?????*?@param?values?An?array?of?Promises.
?????*?@returns?A?new?Promise.
?????*/
????allSettled(values:?Iterable):?Promiseextends?PromiseLike???U?:?T>[]>;
}
代碼實(shí)現(xiàn):
class?MyPromise?{
??static?allSettledextends?readonly?unknown[]?|?readonly?[unknown]>(
????values:?T
??):?MyPromise<
????{
??????-readonly?[P?in?keyof?T]:?PromiseSettledResult<
????????T[P]?extends?PromiseLike???U?:?T[P]
??????>
????}
??>
??static?allSettled(
????values:?Iterable
??):?MyPromiseextends?PromiseLike???U?:?T>[]>
??//?重載函數(shù)的返回值有沖突,想不報(bào)錯(cuò)需要使用聯(lián)合類(lèi)型,這邊圖省事直接用?any?了
??static?allSettled(values:?Iterable):?MyPromise<any>?{
????//?大體寫(xiě)法參照?Promise.all()
????return?new?MyPromise((reslove)?=>?{
??????const?resultArr:?any[]?=?[]
??????const?doneArr:?boolean[]?=?[]
??????//?獲取迭代器
??????const?iter?=?values[Symbol.iterator]()
??????//?當(dāng)前值
??????let?cur?=?iter.next()
??????const?resolveResult?=?(value:?any,?index:?number,?done?:?boolean)?=>?{
????????resultArr[index]?=?{
??????????status:?Status.FULFILLED,
??????????value
????????}
????????doneArr[index]?=?true
????????if?(done?&&?doneArr.every((item)?=>?item))?{
??????????reslove(resultArr)
????????}
??????}
??????for?(let?i?=?0;?!cur.done;?i++)?{
????????const?value?=?cur.value
????????doneArr.push(false)
????????cur?=?iter.next()
????????if?(isPromise(value))?{
??????????value.then(
????????????(value)?=>?{
??????????????resolveResult(value,?i,?cur.done)
????????????},
????????????(reason)?=>?{
??????????????//?這里和?resolve?基本也沒(méi)什么區(qū)別,修改一下?tīng)顟B(tài)和屬性就ok了
??????????????resultArr[i]?=?{
????????????????status:?Status.REJECTED,
????????????????reason
??????????????}
??????????????doneArr[i]?=?true
??????????????if?(cur.done?&&?doneArr.every((item)?=>?item))?{
????????????????reslove(resultArr)
??????????????}
????????????}
??????????)
??????????//?不是?thenable?直接存儲(chǔ)
????????}?else?{
??????????resolveResult(value,?i,?cur.done)
????????}
??????}
????})
??}
}
Promise.any
ESNEXT提出,還處于實(shí)驗(yàn)版本,只有少部分瀏覽器支持
Promise.any(iterable) 方法接收一個(gè) iterable 對(duì)象,返回一個(gè) Promise 實(shí)例。只要iterable中的一個(gè) thenable 的狀態(tài)為fulfilled,就返回那個(gè)thenable所reslove的值,并且該實(shí)例的狀態(tài)也為fulfilled。如果iterable中全為thenable并且狀態(tài)全部為rejected,該實(shí)例的狀態(tài)也為rejected并且reject一個(gè)AggregateError類(lèi)型的實(shí)例(它是 Error 的一個(gè)子類(lèi),用于把單一的錯(cuò)誤集合在一起,目前還在實(shí)驗(yàn)階段,只有少部分瀏覽器支持)。本質(zhì)上,這個(gè)方法和Promise.all()是相反的。
具體用法:
const?pErr?=?new?Promise((resolve,?reject)?=>?{
??reject("總是失敗");
});
const?pSlow?=?new?Promise((resolve,?reject)?=>?{
??setTimeout(resolve,?500,?"最終完成");
});
const?pFast?=?new?Promise((resolve,?reject)?=>?{
??setTimeout(resolve,?100,?"很快完成");
});
Promise.any([pErr,?pSlow,?pFast]).then((value)?=>?{
??console.log(value);
??//?pFast?fulfils?first
})
//?打印結(jié)果為:?"很快完成"
接口定義:
interface?PromiseConstructor?{
???/**
?????*?The?any?function?returns?a?promise?that?is?fulfilled?by?the?first?given?promise?to?be?fulfilled,?or?rejected?with?an?AggregateError?containing?an?array?of?rejection?reasons?if?all?of?the?given?promises?are?rejected.?It?resolves?all?elements?of?the?passed?iterable?to?promises?as?it?runs?this?algorithm.
?????*?@param?values?An?array?or?iterable?of?Promises.
?????*?@returns?A?new?Promise.
?????*/
??any(values:?(T?|?PromiseLike)[]?|?Iterable>):?Promise
}
代碼實(shí)現(xiàn):
class?MyPromise?{
???static?any(
????values:?(T?|?PromiseLike)[]?|?Iterable>
??):?MyPromise?{
????return?new?MyPromise((resolve,?reject)?=>?{
??????//?接收迭代器
??????const?iter?=?values[Symbol.iterator]()
??????let?cur?=?iter.next()
??????const?doneArr:?boolean[]?=?[]
??????for?(let?i?=?0;?!cur.done;?i++)?{
????????const?value?=?cur.value
????????cur?=?iter.next()
????????doneArr.push(false)
????????if?(isPromise(value))?{
???????????//?如果為?thenable,根據(jù)該?thenable?的狀態(tài)進(jìn)行判斷
??????????value.then(resolve,?()?=>?{
????????????doneArr[i]?=?true
??????????????//?只有傳入迭代器的值全是?thenable?并且?thenable?的狀態(tài)全部為?rejected?才會(huì)觸發(fā)
????????????if?(cur.done?&&?doneArr.every((item)?=>?item))?{
??????????????//?應(yīng)該拋出?AggregateError?的錯(cuò)誤類(lèi)型,但是因?yàn)?AggregateError?因?yàn)槭菍?shí)驗(yàn)版本,所有只有最新版瀏覽器才會(huì)有,我這里就用?Error?代替了
??????????????const?e?=?new?Error('All?promises?were?rejected')
??????????????e.stack?=?''
??????????????reject(e)
????????????}
??????????})
????????}?else?{
??????????resolve(value)
????????}
??????}
????})
??}
}
總結(jié)
本文使用 typescript,根據(jù)類(lèi)型定義從零開(kāi)始實(shí)現(xiàn)了一個(gè) Promise,其中重點(diǎn)深入了對(duì)于then方法的處理。雖不完美,但也算達(dá)到了想要的效果。作者技術(shù)有限,如果有什么錯(cuò)誤或遺漏的地方還請(qǐng)?jiān)谠u(píng)論區(qū)中指出,順便求個(gè)??。
文章的代碼已上傳至github:
https://github.com/Col0ring/learning-es6/tree/main/Promise
