版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第萬(wàn)字詳解JavaScript手寫(xiě)一個(gè)Promise目錄前言Promise核心原理實(shí)現(xiàn)Promise的使用分析MyPromise的實(shí)現(xiàn)在Promise中加入異步操作實(shí)現(xiàn)then方法的多次調(diào)用實(shí)現(xiàn)then的鏈?zhǔn)秸{(diào)用then方法鏈?zhǔn)秸{(diào)用識(shí)別Promise對(duì)象自返回捕獲錯(cuò)誤及then鏈?zhǔn)秸{(diào)用其他狀態(tài)代碼補(bǔ)充捕獲執(zhí)行器錯(cuò)誤捕獲then中的報(bào)錯(cuò)錯(cuò)誤與異步狀態(tài)的鏈?zhǔn)秸{(diào)用將then方法的參數(shù)變成可選參數(shù)Promise.all方法的實(shí)現(xiàn)Promise.resolve方法的實(shí)現(xiàn)finally方法的實(shí)現(xiàn)catch方法的實(shí)現(xiàn)完整代碼
前言
手寫(xiě)Promise現(xiàn)在已經(jīng)成了面試的熱門(mén)內(nèi)容,但在實(shí)際開(kāi)發(fā)中基本都不會(huì)去手寫(xiě)一個(gè)Promise,但是在面試中各種手寫(xiě)題可能就會(huì)遇到一個(gè)手寫(xiě)Promise,我們可以盡量提高我們的上限,從而獲取更多的工作機(jī)會(huì)。
Promise核心原理實(shí)現(xiàn)
首先我們從使用的角度來(lái)分析一下Promise,然后編寫(xiě)一個(gè)最簡(jiǎn)單版本的Promise。
Promise的使用分析
Promise就是一個(gè)類(lèi),在執(zhí)行這個(gè)類(lèi)的時(shí)候,需要傳遞一個(gè)執(zhí)行器(回調(diào)函數(shù))進(jìn)去,執(zhí)行器會(huì)立即執(zhí)行。
Promise中的狀態(tài)分為三個(gè),分別是:
pending等待fulfilled成功rejected失敗
狀態(tài)的切換只有兩種,分別是:
pendingfulfilledpendingrejected
一旦狀態(tài)發(fā)生改變,就不會(huì)再次改變:
執(zhí)行器中的兩個(gè)參數(shù),分別是resolve和reject,其實(shí)就是兩個(gè)回調(diào)函數(shù),調(diào)用resolve是從pending狀態(tài)到fulfilled,調(diào)用reject是從狀態(tài)pending到rejected。傳遞給這兩個(gè)回調(diào)函數(shù)的參數(shù)會(huì)作為成功或失敗的值。Promise的實(shí)例對(duì)象具有一個(gè)then方法,該方法接受兩個(gè)回調(diào)函數(shù),分別來(lái)處理成功與失敗的狀態(tài),then方法內(nèi)部會(huì)進(jìn)行判斷,然后根據(jù)當(dāng)前狀態(tài)確定調(diào)用的回調(diào)函數(shù)。then方法應(yīng)該是被定義在原型對(duì)象中的。then的回調(diào)函數(shù)中都包含一個(gè)值,如果是成功,表示成功后返回的值;如果是失敗就表示失敗的原因。
MyPromise的實(shí)現(xiàn)
根據(jù)我們上面的分析,寫(xiě)出如下代碼:
MyPromise.js
//定義所有狀態(tài)的常量
constPENDING='pending'
constFULFILLED='fulfilled'
constREJECTED='rejected'
//Promise實(shí)質(zhì)上就是一個(gè)類(lèi),首先創(chuàng)建一個(gè)Promise的類(lèi)
classMyPromise{
//實(shí)例化Promise時(shí)需要一個(gè)回調(diào)函數(shù),該回調(diào)函數(shù)立即執(zhí)行
constructor(executor){
//在調(diào)用executor需要傳遞兩個(gè)回調(diào)函數(shù),分別是resolve和reject
executor(this.resolve,this.reject)
//Promise的狀態(tài)
status=PENDING
//記錄成功與失敗的值
value=undefined
reason=undefined
resolve=(value)={//這里使用箭頭函數(shù)是為了使其內(nèi)部的this指向?yàn)樵搶?shí)例化后的對(duì)象
//形參value表示,調(diào)用resolve時(shí)傳遞的參數(shù)
//如果當(dāng)前狀態(tài)不是pending,就直接跳出該邏輯
if(this.status!==PENDING)return
//將狀態(tài)修改為成功
this.status=FULFILLED
//將傳入的值進(jìn)行保存
this.value=value
reject=(reason)={//這里使用箭頭函數(shù)是為了使其內(nèi)部的this指向?yàn)樵搶?shí)例化后的對(duì)象
//形參reason表示,調(diào)用reject時(shí)傳遞的失敗的原因
//如果當(dāng)前狀態(tài)不是pending,就直接跳出該邏輯
if(this.status!==PENDING)return
//將狀態(tài)修改為失敗
this.status=REJECTED
//保存失敗的原因
this.reason=reason
//then方法的實(shí)現(xiàn)
then(onFulfilled,onRejected){
//判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
if(this.status===FULFILLED){
//將成功的值作為參數(shù)返回
onFulfilled(this.value)
}elseif(this.status===REJECTED){
//將失敗的原因作為參數(shù)返回
onRejected(this.reason)
//導(dǎo)出Promise
module.exports=MyPromise
現(xiàn)在我們就來(lái)寫(xiě)一段代碼驗(yàn)證一下上面的代碼
驗(yàn)證resolve:
constMyPromise=require('./myPromise')
letpromise=newMyPromise((resolve,reject)={
resolve('成功')
promise.then(value={
console.log(value);
},reason={
console.log(reason);
/*輸出
*/
驗(yàn)證reject:
constMyPromise=require('./myPromise')
letpromise=newMyPromise((resolve,reject)={
reject('失敗')
promise.then(value={
console.log(value);
},reason={
console.log(reason);
/*輸出
*/
驗(yàn)證狀態(tài)不可變:
constMyPromise=require('./myPromise')
letpromise=newMyPromise((resolve,reject)={
resolve('成功')
reject('失敗')
promise.then(value={
console.log(value);
},reason={
console.log(reason);
/*輸出
*/
在Promise中加入異步操作
如果我們的代碼中存在異步操作,我們自己寫(xiě)的Promise將毫無(wú)用處,
例如下面這段代碼:
constMyPromise=require('./myPromise')
letpromise=newMyPromise((resolve,reject)={
setTimeout(resolve,2000,'成功')
promise.then(value={
console.log(value);
},reason={
console.log(reason);
})
這段代碼2000ms后沒(méi)有任何輸出,為了解決這個(gè)問(wèn)題我們將對(duì)自己編寫(xiě)的類(lèi)進(jìn)行如下操作:
創(chuàng)建兩個(gè)實(shí)例方法用于存儲(chǔ)我們傳入的成功與失敗的處理邏輯。為then方法添加狀態(tài)為pending時(shí)的處理邏輯,這時(shí)將傳遞進(jìn)來(lái)的屬性保存到實(shí)例上。在成功或者失敗時(shí)調(diào)用相應(yīng)的傳遞進(jìn)來(lái)的回調(diào)函數(shù)(實(shí)例屬性存在函數(shù)的情況下)。
實(shí)現(xiàn)代碼如下:
//MyPromise.js
constPENDING='pending'
constFULFILLED='fulfilled'
constREJECTED='rejected'
classMyPromise{
constructor(executor){
executor(this.resolve,this.reject)
status=PENDING
value=undefined
reason=undefined
//存儲(chǔ)成功與失敗的處理邏輯
onFulfilled=undefined
onRejected=undefined
resolve=(value)={
if(this.status!==PENDING)return
this.status=FULFILLED
this.value=value
//如果將狀態(tài)修復(fù)為成功,調(diào)用成功的回調(diào)
this.onFulfilledthis.onFulfilled(this.value)
reject=(reason)={
if(this.status!==PENDING)return
this.status=REJECTED
this.reason=reason
//如果將狀態(tài)修復(fù)為失敗,調(diào)用失敗的回調(diào)
this.onRejectedthis.onRejected(this.reason)
then(onFulfilled,onRejected){
if(this.status===FULFILLED){
onFulfilled(this.value)
}elseif(this.status===REJECTED){
onRejected(this.reason)
}else{
//表示既不是成功,也不是失敗。這個(gè)時(shí)候保存?zhèn)鬟f進(jìn)來(lái)的兩個(gè)回調(diào)
this.onFulfilled=onFulfilled
this.onRejected=onRejected
module.exports=MyPromise
這里的this.onFulfilledthis.onFulfilled(this.value)表示如果該屬性存在就調(diào)用這個(gè)方法。
現(xiàn)在我們重新執(zhí)行一開(kāi)始上面那一段代碼,2s后會(huì)成功輸出成功。
實(shí)現(xiàn)then方法的多次調(diào)用
Promise實(shí)例中存在要給then方法,允許我們?cè)赑romise實(shí)例中鏈?zhǔn)秸{(diào)用,每個(gè)then方法還會(huì)返回一個(gè)Promise實(shí)例,
如下圖所示:
Promise實(shí)例方法then是可以多次進(jìn)行調(diào)用,而我們現(xiàn)在自己封裝的卻執(zhí)行調(diào)用一次,現(xiàn)在根據(jù)新需要來(lái)重新改寫(xiě)我們的代碼,
實(shí)現(xiàn)思路如下:
定義可以存儲(chǔ)多個(gè)回調(diào)的數(shù)組,用于存儲(chǔ)多個(gè)回調(diào)函數(shù)。如果是同步執(zhí)行的代碼,執(zhí)行后立即知道執(zhí)行結(jié)果,所以可以直接調(diào)用回調(diào)函數(shù)。如果是異步代碼,需要將每次回調(diào)函數(shù)保存到數(shù)組中,然后狀態(tài)變化時(shí)依次調(diào)用函數(shù)。
實(shí)現(xiàn)代碼如下:
//MyPromise.js
constPENDING='pending'
constFULFILLED='fulfilled'
constREJECTED='rejected'
classMyPromise{
constructor(executor){
executor(this.resolve,this.reject)
status=PENDING
value=undefined
reason=undefined
//存儲(chǔ)成功與失敗的處理邏輯
onFulfilled=[]
onRejected=[]
resolve=(value)={
if(this.status!==PENDING)return
this.status=FULFILLED
this.value=value
//如果將狀態(tài)修復(fù)為成功,調(diào)用成功的回調(diào)
while(this.onFulfilled.length){
//Atotype.shift()用于刪除數(shù)組第一個(gè)元素,并返回
this.onFulfilled.shift()(this.value)
reject=(reason)={
if(this.status!==PENDING)return
this.status=REJECTED
this.reason=reason
//如果將狀態(tài)修復(fù)為失敗,調(diào)用失敗的回調(diào)
while(this.onRejected.length){
//Atotype.shift()用于刪除數(shù)組第一個(gè)元素,并返回
this.onRejected.shift()(this.reason)
then(onFulfilled,onRejected){
if(this.status===FULFILLED){
onFulfilled(this.value)
}elseif(this.status===REJECTED){
onRejected(this.reason)
}else{
//表示既不是成功,也不是失敗。這個(gè)時(shí)候保存?zhèn)鬟f進(jìn)來(lái)的兩個(gè)回調(diào)
this.onFulfilled.push(onFulfilled)
this.onRejected.push(onRejected)
module.exports=MyPromise
這里我們通過(guò)數(shù)組的shift()方法,該方法刪除數(shù)組的第一個(gè)元素,并返回,返回的這個(gè)值正好是一個(gè)回調(diào)函數(shù),然后調(diào)用該函數(shù)即可實(shí)現(xiàn)該功能。
實(shí)現(xiàn)then的鏈?zhǔn)秸{(diào)用
想要實(shí)現(xiàn)then的鏈?zhǔn)秸{(diào)用,主要解決兩個(gè)問(wèn)題:
返回的是一個(gè)新的MyPormise實(shí)例。then的返回值作為下一次的鏈?zhǔn)秸{(diào)用的參數(shù)。
這里分為兩種情況:
直接返回一個(gè)值,可以直接作為值使用返回一個(gè)新的MyPormise實(shí)例,此時(shí)就需要判斷其狀態(tài)
實(shí)現(xiàn)代碼如下:
//MyPromise.js
/*省略的代碼同上*/
classMyPromise{
/*省略的代碼同上*/
//then方法的實(shí)現(xiàn)
then(onFulfilled,onRejected){
//then方法返回一個(gè)MyPromise實(shí)例
returnnewMyPromise((resolve,reject)={
//判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
if(this.status===FULFILLED){
//將成功的值作為參數(shù)返回
//保存執(zhí)行回調(diào)函數(shù)的結(jié)果
constresult=onFulfilled(this.value)
//如果返回的是一個(gè)普通的值,直接調(diào)用resolve
//如果是一個(gè)MyPromise實(shí)例,根據(jù)返回的解決來(lái)決定是調(diào)用resolve,還是reject
resolvePromise(result,resolve,reject)
}elseif(this.status===REJECTED){
//將失敗的原因作為參數(shù)返回
onRejected(this.reason)
}else{
//表示既不是成功,也不是失敗。這個(gè)時(shí)候保存?zhèn)鬟f進(jìn)來(lái)的兩個(gè)回調(diào)
this.onFulfilled.push(onFulfilled)
this.onRejected.push(onRejected)
functionresolvePromise(result,resolve,reject){
//判斷傳遞的result是不是MyPromise的實(shí)例對(duì)象
if(resultinstanceofMyPromise){
//說(shuō)明是MyPromise的實(shí)例對(duì)象
//調(diào)用.then方法,然后在回調(diào)函數(shù)中獲取到具體的值,然后調(diào)用具體的回調(diào)
//result.then(value=resolve(value),reason=reject(reason))
//簡(jiǎn)寫(xiě)
result.then(resolve,reject)
}else{
resolve(result)
module.exports=MyPromise
then方法鏈?zhǔn)秸{(diào)用識(shí)別Promise對(duì)象自返回
在Promise中,如果then方法返回的是自己的promise對(duì)象,則會(huì)發(fā)生promise的嵌套,這個(gè)時(shí)候程序會(huì)報(bào)錯(cuò),
測(cè)試代碼如下:
varpromise=newPromise((resolve,reject)={
resolve(100)
varp1=promise.then(value={
console.log(value)
returnp1
//100
//Uncaught(inpromise)TypeError:Chainingcycledetectedforpromise#Promise
想要解決這個(gè)問(wèn)題其實(shí)我們只需要在then方法返回的MyPromise實(shí)例對(duì)象與then中回調(diào)函數(shù)返回的值進(jìn)行比對(duì),如果相同的返回一個(gè)reject的MyPromise實(shí)例對(duì)象,并創(chuàng)建一個(gè)TypeError類(lèi)型的Error。
實(shí)現(xiàn)代碼如下:
//MyPromise.js
/*省略的代碼同上*/
then(onFulfilled,onRejected){
//then方法返回一個(gè)MyPromise實(shí)例
constpromise=newMyPromise((resolve,reject)={
//判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
if(this.status===FULFILLED){
//這里并不需要延遲執(zhí)行,而是通過(guò)setTimeout將其變成異步函數(shù)
//如果不變成異步的話(huà)是在函數(shù)內(nèi)獲取不到promise的
setTimeout(()={
//將成功的值作為參數(shù)返回
//保存執(zhí)行回調(diào)函數(shù)的結(jié)果
constresult=onFulfilled(this.value)
//如果返回的是一個(gè)普通的值,直接調(diào)用resolve
//如果是一個(gè)MyPromise實(shí)例,根據(jù)返回的解決來(lái)決定是調(diào)用resolve,還是reject
resolvePromise(promise,result,resolve,reject)
},0)
}elseif(this.status===REJECTED){
//將失敗的原因作為參數(shù)返回
onRejected(this.reason)
}else{
//表示既不是成功,也不是失敗。這個(gè)時(shí)候保存?zhèn)鬟f進(jìn)來(lái)的兩個(gè)回調(diào)
this.onFulfilled.push(onFulfilled)
this.onRejected.push(onRejected)
returnpromise
functionresolvePromise(promise,result,resolve,reject){
//這里修改一下該函數(shù),如果return的Promise實(shí)例對(duì)象,也就是傳入的promise===result的話(huà),說(shuō)明在promise中return的是當(dāng)前promise對(duì)象。
if(promise===result){
//這里調(diào)用reject,并拋出一個(gè)Error
//return是必須的,阻止程序向下執(zhí)行
returnreject(newTypeError('Chainingcycledetectedforpromise#Promise'))
//判斷傳遞的result是不是MyPromise的實(shí)例對(duì)象
if(resultinstanceofMyPromise){
//說(shuō)明是MyPromise的實(shí)例對(duì)象
//調(diào)用.then方法,然后在回調(diào)函數(shù)中獲取到具體的值,然后調(diào)用具體的回調(diào)
//result.then(value=resolve(value),reason=reject(reason))
//簡(jiǎn)寫(xiě)
result.then(resolve,reject)
}else{
resolve(result)
module.exports=MyPromise
這里then方法中的setTimeout的作用并不是延遲執(zhí)行,而是為了調(diào)用resolvePromise函數(shù)時(shí),保證創(chuàng)建的promise存在。
捕獲錯(cuò)誤及then鏈?zhǔn)秸{(diào)用其他狀態(tài)代碼補(bǔ)充
到目前為止我們現(xiàn)實(shí)的Promise并沒(méi)有對(duì)異常做任何處理,為了保證代碼的健壯性,我們需要對(duì)異常做一些處理。
捕獲執(zhí)行器錯(cuò)誤
現(xiàn)在我們需要對(duì)執(zhí)行器進(jìn)行異常捕獲,如果發(fā)生異常,就將我們的狀態(tài)修改為rejected。
捕獲執(zhí)行器的錯(cuò)誤也比較簡(jiǎn)單,只需要在構(gòu)造函數(shù)中加入try...catch語(yǔ)句就可以,
實(shí)現(xiàn)代碼如下:
constructor(executor){
try{
//在調(diào)用executor需要傳遞兩個(gè)回調(diào)函數(shù),分別是resolve和reject
executor(this.resolve,this.reject)
}catch(e){
//發(fā)生異常調(diào)用reject
this.reject(e)
}
測(cè)試代碼如下:
constMyPromise=require('./myPromise')
letpromise=newMyPromise((resolve,reject)={
thrownewError('執(zhí)行器錯(cuò)誤')
promise.then(value={
console.log(value);
},error={
console.log(error.message);
/*輸出
執(zhí)行器錯(cuò)誤
*/
捕獲then中的報(bào)錯(cuò)
現(xiàn)在我們需要對(duì)then中的異常捕獲到,并在下一次鏈?zhǔn)秸{(diào)用中傳遞到then的第二個(gè)函數(shù)中,實(shí)現(xiàn)的方式也是通過(guò)try...catch語(yǔ)句,
示例代碼如下:
//then方法的實(shí)現(xiàn)
then(onFulfilled,onRejected){
//then方法返回一個(gè)MyPromise實(shí)例
constpromise=newMyPromise((resolve,reject)={
//判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
if(this.status===FULFILLED){
//這里并不需要延遲執(zhí)行,而是通過(guò)setTimeout將其變成異步函數(shù)
//如果不變成異步的話(huà)是在函數(shù)內(nèi)獲取不到promise的
setTimeout(()={
try{
//將成功的值作為參數(shù)返回
//保存執(zhí)行回調(diào)函數(shù)的結(jié)果
constresult=onFulfilled(this.value)
//如果返回的是一個(gè)普通的值,直接調(diào)用resolve
//如果是一個(gè)MyPromise實(shí)例,根據(jù)返回的解決來(lái)決定是調(diào)用resolve,還是reject
resolvePromise(promise,result,resolve,reject)
}catch(error){
reject(error)
},0)
}elseif(this.status===REJECTED){
//將失敗的原因作為參數(shù)返回
onRejected(this.reason)
}else{
//表示既不是成功,也不是失敗。這個(gè)時(shí)候保存?zhèn)鬟f進(jìn)來(lái)的兩個(gè)回調(diào)
this.onFulfilled.push(onFulfilled)
this.onRejected.push(onRejected)
returnpromise
}
測(cè)試代碼如下:
constMyPromise=require('./myPromise')
letpromise=newMyPromise((resolve,reject)={
resolve('成功')
//第一個(gè)then方法中的錯(cuò)誤要在第二個(gè)then方法中捕獲到
promise.then(value={
console.log('resolve',value)
thrownewError('then的執(zhí)行過(guò)程中遇到異常')
}).then(null,reason={
console.log(reason.message)
/*輸出
resolve成功
then的執(zhí)行過(guò)程中遇到異常
*/
錯(cuò)誤與異步狀態(tài)的鏈?zhǔn)秸{(diào)用
現(xiàn)在只對(duì)成功狀態(tài)的then進(jìn)行的鏈?zhǔn)秸{(diào)用以及錯(cuò)誤處理,錯(cuò)誤與異步狀態(tài)未進(jìn)行處理,其實(shí)處理起來(lái)也是一樣的,
示例代碼如下:
//then方法的實(shí)現(xiàn)
then(onFulfilled,onRejected){
//then方法返回一個(gè)MyPromise實(shí)例
constpromise=newMyPromise((resolve,reject)={
//判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
if(this.status===FULFILLED){
//這里并不需要延遲執(zhí)行,而是通過(guò)setTimeout將其變成異步函數(shù)
//如果不變成異步的話(huà)是在函數(shù)內(nèi)獲取不到promise的
setTimeout(()={
try{
//將成功的值作為參數(shù)返回
//保存執(zhí)行回調(diào)函數(shù)的結(jié)果
constresult=onFulfilled(this.value)
//如果返回的是一個(gè)普通的值,直接調(diào)用resolve
//如果是一個(gè)MyPromise實(shí)例,根據(jù)返回的解決來(lái)決定是調(diào)用resolve,還是reject
resolvePromise(promise,result,resolve,reject)
}catch(error){
reject(error)
},0)
}elseif(this.status===REJECTED){
//失敗的處理同成功處理,只是調(diào)用的回調(diào)函數(shù)不同
setTimeout(()={
try{
constresult=onRejected(this.reason)
resolvePromise(promise,result,resolve,reject)
}catch(error){
reject(error)
},0)
}else{
this.onFulfilled.push((value)={
setTimeout(()={
try{
constresult=onFulfilled(value)
resolvePromise(promise,result,resolve,reject)
}catch(error){
reject(error)
},0)
this.onRejected.push((reason)={
setTimeout(()={
try{
constresult=onRejected(reason)
resolvePromise(promise,result,resolve,reject)
}catch(error){
reject(error)
},0)
returnpromise
}
測(cè)試代碼如下:
constMyPromise=require('./myPromise')
letpromise=newMyPromise((resolve,reject)={
setTimeout(resolve,2000,'成功')
//第一個(gè)then方法中的錯(cuò)誤要在第二個(gè)then方法中捕獲到
promise.then(value={
console.log('resolve',value)
thrownewError('then的執(zhí)行過(guò)程中遇到異常')
}).then(null,reason={
console.log(reason.message)
/*輸出
resolve成功
then的執(zhí)行過(guò)程中遇到異常
*/
將then方法的參數(shù)變成可選參數(shù)
Promise中的then方法其實(shí)是兩個(gè)可以可選參數(shù),如果我們不傳遞任何參數(shù)的話(huà),里面的結(jié)果是向下傳遞的,直到捕獲為止,
例如下面這段代碼:
newPromise((resolve,reject)={
resolve(100)
.then()
.then()
.then()
.then(value=console.log(value))
//最后一個(gè)then輸入100
這段代碼可以理解為:
newPromise((resolve,reject)={
resolve(100)
.then(value=value)
.then(value=value)
.then(value=value)
.then(value=console.log(value))
所以說(shuō)我們只需要在沒(méi)有傳遞回調(diào)函數(shù)時(shí),賦值一個(gè)默認(rèn)的回調(diào)函數(shù)即可。
實(shí)現(xiàn)代碼如下:
//then方法的實(shí)現(xiàn)
then(onFulfilled,onRejected){
//如果傳遞函數(shù),就是用傳遞的函數(shù),否則指定一個(gè)默認(rèn)值,用于參數(shù)傳遞
onFulfilled=onFulfilledonFulfilled:value=value
//同理
onRejected=onRejectedonRejected:reason={throwreason}
//then方法返回一個(gè)MyPromise實(shí)例
constpromise=newMyPromise((resolve,reject)={
//判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
if(this.status===FULFILLED){...
}elseif(this.status===REJECTED){...
}else{...
returnpromise
}
Promise.all方法的實(shí)現(xiàn)
關(guān)于all()方法的使用,可以參數(shù)Promise.all()。簡(jiǎn)單的說(shuō)Promise.all()會(huì)將多個(gè)Promise實(shí)例包裝為一個(gè)Promise實(shí)例,且順序與調(diào)用順序一致,
示例代碼如下:
functionp1(){
returnnewPromise((resolve,reject)={
setTimeout(()={
resolve('p1')
},2000)
functionp2(){
returnnewPromise((resolve,reject)={
setTimeout(()={
resolve('p2')
},0)
Promise.all(['a','b',p1(),p2(),'c']).then(result={
console.log(result)
//["a","b","p1","p2","c"]
})
在這段代碼中,我們的p1的執(zhí)行是延遲了2s的,這里如果不使用Promise.all()的話(huà)最終順序是與我們調(diào)用不同的。
現(xiàn)在我們來(lái)分析一下all()的實(shí)現(xiàn)思路:
all()方法可以通過(guò)類(lèi)直接調(diào)用,所以是一個(gè)靜態(tài)方法all()方法接收一個(gè)數(shù)組,數(shù)組中的值可以是一個(gè)普通值,也可以是一個(gè)MyPromise的實(shí)例對(duì)象return一個(gè)新的MyPromise實(shí)例對(duì)象遍歷數(shù)組中的每一個(gè)值,判斷值得類(lèi)型,如果是一個(gè)普通值得話(huà)直接將值存入一個(gè)數(shù)組;如果是一個(gè)MyPromise的實(shí)例對(duì)象的話(huà),會(huì)調(diào)用then方法,然后根據(jù)執(zhí)行后的狀態(tài),如果失敗的話(huà)調(diào)用新的MyPromise實(shí)例對(duì)象中的rejecte,如果是成功話(huà)將這個(gè)值存入一個(gè)數(shù)組存入數(shù)組時(shí)計(jì)數(shù),如果存入的數(shù)量達(dá)到傳入的數(shù)組長(zhǎng)度,說(shuō)明調(diào)用完畢,執(zhí)行resolve并將最終的結(jié)果數(shù)組作為參數(shù)返回。
實(shí)現(xiàn)代碼:
/**
*@description:將多個(gè)Promise實(shí)例合并為一個(gè)Promise實(shí)例
*@param{*}arrayPromise或者普通值
*@returns{Promise}
staticall(array){
//用于存放最終結(jié)果的數(shù)組
letresult=[]
//用于計(jì)算當(dāng)前已經(jīng)執(zhí)行完的實(shí)例的數(shù)量
letcount=0
//最后返回的是一個(gè)Promise實(shí)例
returnnewMyPromise((resolve,reject)={
/**
*@description:將執(zhí)行完畢的值加入結(jié)果數(shù)組,并根據(jù)實(shí)際情況決定是否調(diào)用resolve
*@param{*}result存放結(jié)果的數(shù)組
*@param{*}index要加入的索引
*@param{*}value要加入數(shù)組的值
*@param{*}resolvePromise中的resolve
functionaddResult(result,index,value,resolve){
//根據(jù)索引值,將結(jié)果堆入數(shù)組中
result[index]=value
//執(zhí)行完畢一個(gè)count+1,如果當(dāng)前值等于總長(zhǎng)度的話(huà)說(shuō)明已經(jīng)執(zhí)行結(jié)束了,可以直接調(diào)用resolve,說(shuō)明已經(jīng)成功執(zhí)行完畢了
if(++count===array.length){
//將執(zhí)行結(jié)果返回
resolve(result)
//遍歷穿入的數(shù)組,每個(gè)都執(zhí)行then方法,獲取到最終的結(jié)果
array.forEach((p,index)={
//判斷p是不是MyPromise的實(shí)例,如果是的話(huà)調(diào)用then方法,不是直接將值加入數(shù)組中
if(pinstanceofMyPromise){
p.then(
//成功時(shí)將結(jié)果直接加入數(shù)組中
value={
addResult(result,index,value,resolve)
//如果失敗直接返回失敗原因
reason={
reject(reason)
else{
addResult(result,index,p,resolve)
}
Promise.resolve方法的實(shí)現(xiàn)
關(guān)于Promise.resolve()方法的用法可以參考Promise.resolve()與Promise.reject()。
我們實(shí)現(xiàn)的思路主要如下:
該方法是一個(gè)靜態(tài)方法該方法接受的如果是一個(gè)值就將該值包裝為一個(gè)MyPromise的實(shí)例對(duì)象返回,如果是一個(gè)MyPromise的實(shí)例對(duì)象,調(diào)用then方法返回。
實(shí)現(xiàn)代碼如下:
staticresolve(value){
//如果是MyPromise的實(shí)例,就直接返回這個(gè)實(shí)例
if(valueinstanceofMyPromise)returnvalue
//如果不是的話(huà)創(chuàng)建一個(gè)MyPromise實(shí)例,并返回傳遞的值
returnnewMyPromise((resolve)={
resolve(value)
}
finally方法的實(shí)現(xiàn)
關(guān)于finally方法可參考finally(),實(shí)現(xiàn)該方法的實(shí)現(xiàn)代碼如下:
finally(callback){
//如何拿到當(dāng)前的promise的狀態(tài),使用then方法,而且不管怎樣都返回callback
//而且then方法就是返回一個(gè)promise對(duì)象,那么我們直接返回then方法調(diào)用之后的結(jié)果即可
//我們需要在回調(diào)之后拿到成功的回調(diào),所以需要把value也return
//失敗的回調(diào)也拋出原因
//如果callback是一個(gè)異步的promise對(duì)象,我們還需要等待其執(zhí)行完畢,所以需要用到靜態(tài)方法resolve
returnthis.then(value={
//把callback調(diào)用之后返回的promise傳遞過(guò)去,并且執(zhí)行promise,且在成功之后返回value
returnMyPromise.resolve(callback()).then(()=value)
},reason={
//失敗之后調(diào)用的then方法,然后把失敗的原因返回出去。
returnMyPromise.resolve(callback()).then(()={throwreason})
}
測(cè)試:
functionp1(){
returnnewMyPromise((resolve,reject)={
setTimeout(()={
resolve('p1')
},2000)
functionp2(){
returnnewMyPromise((resolve,reject)={
reject('p2reject')
p2().finally(
()={
console.log('finallyp2')
returnp1()
).then(
value={
console.log(value)
},reason={
console.log(reason)
//finallyp2
//兩秒之后執(zhí)行p2reject
catch方法的實(shí)現(xiàn)
關(guān)于catch方法可以參考catch(),實(shí)現(xiàn)該方法其實(shí)非常簡(jiǎn)單,只需要在內(nèi)部調(diào)用then方法,不傳遞第一個(gè)回調(diào)函數(shù)即可,
實(shí)現(xiàn)代碼如下:
catch(callback){
returnthis.then(null,failCallback)
}
測(cè)試如下:
functionp(){
returnnewMyPromise((resolve,reject)={
reject(newError('reject'))
.then(value={
console.log(value)
.catch(reason=console.log(reason))
完整代碼
//MyPromise.js
//定義所有狀態(tài)的常量
constPENDING='pending'
constFULFILLED='fulfilled'
constREJECTED='rejected'
//Promise實(shí)質(zhì)上就是一個(gè)類(lèi),首先創(chuàng)建一個(gè)Promise的類(lèi)
classMyPromise{
//實(shí)例化Promise時(shí)需要一個(gè)回調(diào)函數(shù),該回調(diào)函數(shù)立即執(zhí)行
constructor(executor){
try{
//在調(diào)用executor需要傳遞兩個(gè)回調(diào)函數(shù),分別是resolve和reject
executor(this.resolve,this.reject)
}catch(e){
//發(fā)生異常調(diào)用reject
this.reject(e)
//Promise的狀態(tài)
status=PENDING
//記錄成功與失敗的值
value=undefined
reason=undefined
//存儲(chǔ)成功與失敗的處理邏輯
onFulfilled=[]
onRejected=[]
resolve=(value)={//這里使用箭頭函數(shù)是為了使其內(nèi)部的this指向?yàn)樵搶?shí)例化后的對(duì)象
//形參value表示,調(diào)用resolve時(shí)傳遞的參數(shù)
//如果當(dāng)前狀態(tài)不是pending,就直接跳出該邏輯
if(this.status!==PENDING)return
//將狀態(tài)修改為成功
this.status=FULFILLED
//將傳入的值進(jìn)行保存
this.value=value
//如果將狀態(tài)修復(fù)為成功,調(diào)用成功的回調(diào)
//this.onFulfilledthis.onFulfilled(this.value)
while(this.onFulfilled.length){
//Atotype.shift()用于刪除數(shù)組第一個(gè)元素,并返回
this.onFulfilled.shift()(this.value)
reject=(reason)={//這里使用箭頭函數(shù)是為了使其內(nèi)部的this指向?yàn)樵搶?shí)例化后的對(duì)象
//形參reason表示,調(diào)用reject時(shí)傳遞的失敗的原因
//如果當(dāng)前狀態(tài)不是pending,就直接跳出該邏輯
if(this.status!==PENDING)return
//將狀態(tài)修改為失敗
this.status=REJECTED
//保存失敗的原因
this.reason=reason
//如果將狀態(tài)修復(fù)為失敗,調(diào)用失敗的回調(diào)
//this.onRejectedthis.onRejected(this.reason)
while(this.onRejected.length){
//Atotype.shift()用于刪除數(shù)組第一個(gè)元素,并返回
this.onRejected.shift()(this.reason)
//then方法的實(shí)現(xiàn)
then(onFulfilled,onRejected){
//如果傳遞函數(shù),就是用傳遞的函數(shù),否則指定一個(gè)默認(rèn)值,用于參數(shù)傳遞
onFulfilled=onFulfilledonFulfilled:value=value
//同理
onRejected=onRejectedonRejected:reason={throwreason}
//then方法返回一個(gè)MyPromise實(shí)例
constpromise=newMyPromise((resolve,reject)={
//判斷當(dāng)前狀態(tài),根據(jù)狀態(tài)調(diào)用指定回調(diào)
if(this.status===FULFILLED){
//這里并不需要延遲執(zhí)行,而是通過(guò)setTimeout將其變成異步函數(shù)
//如果不變成異步的話(huà)是在函數(shù)內(nèi)獲取不到promise的
setTimeout(()={
try{
//將成功的值作為參數(shù)返回
//保存執(zhí)行回調(diào)函數(shù)的結(jié)果
constresult=onFulfilled(this.value)
//如果返回的是一個(gè)普通的值,直接調(diào)用resolve
//如果是一個(gè)MyPromise實(shí)例,根據(jù)返回的解決來(lái)決定是調(diào)用resolve,還是reject
resolvePromise(promise,result,resolve,reject)
}catch(error){
reject(error)
},0)
}elseif(this.status===REJECTED){
//失敗的處理同成功處理,只是調(diào)用的回調(diào)函數(shù)不同
setTimeout(()={
try{
constresult=onRejected(this.reason)
resolvePromise(promise,result,resolve,reject)
}catch(error){
reject(error)
},0)
}else{
//表示既不是成功,也不是失敗。這個(gè)時(shí)候保存?zhèn)鬟f進(jìn)來(lái)的兩個(gè)回調(diào)
//this.onFulfilled.push(onFulfilled)
//this.onRejected.push(onRejected)
this.onFulfilled.push((value)={
setTimeout(()={
try{
constresult=onFulfilled(value)
resolvePromise(promise,result,resolve,reject)
}catch(error){
reject(error)
},0)
this.onRejected.push((reason)={
setTimeout(()={
try{
constresult=onRejected(reason)
resolvePromise(promise,result,resolve,reject)
}catch(error){
reject(error)
},0)
returnpromise
catch(callback){
returnthis.then(null,callback)
finally(callback){
//如何拿到當(dāng)前的promise的狀態(tài),使用then方法,而且不管怎樣都返回callback
//而且then方法就是返回一個(gè)promise對(duì)象,那么我們直接返回then方法調(diào)用之后的結(jié)果即可
//我們需要在回調(diào)之后拿到成功的回調(diào),所以需要把val
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025-2026人教版小學(xué)二年級(jí)語(yǔ)文期末測(cè)試卷上
- 結(jié)構(gòu)專(zhuān)業(yè)考試題及答案
- 腸狹窄術(shù)后腹腔感染的處理策略
- 腸梗阻合并糖尿病患者的血糖管理策略
- 衛(wèi)生院執(zhí)業(yè)監(jiān)督管理制度
- 衛(wèi)生站器械管理制度
- 約束鄉(xiāng)鎮(zhèn)衛(wèi)生院制度
- 學(xué)校直飲機(jī)衛(wèi)生管理制度
- 攪拌站環(huán)境衛(wèi)生管理制度
- 衛(wèi)生所用電安全管理制度
- 安全生產(chǎn)目標(biāo)及考核制度
- (2026版)患者十大安全目標(biāo)(2篇)
- 2026年北大拉丁語(yǔ)標(biāo)準(zhǔn)考試試題
- 臨床護(hù)理操作流程禮儀規(guī)范
- 2025年酒店總經(jīng)理年度工作總結(jié)暨戰(zhàn)略規(guī)劃
- 空氣栓塞課件教學(xué)
- 2025年國(guó)家市場(chǎng)監(jiān)管總局公開(kāi)遴選公務(wù)員面試題及答案
- 肌骨康復(fù)腰椎課件
- 2025年10月自考04184線性代數(shù)經(jīng)管類(lèi)試題及答案含評(píng)分參考
- 2025年勞動(dòng)保障協(xié)理員三級(jí)技能試題及答案
- 20以?xún)?nèi)加減法混合口算練習(xí)題1000道(附答案)
評(píng)論
0/150
提交評(píng)論