手寫Promise太難疙渣?那是你不會拆分需求匙奴!
現在面試也越來越
內卷
了,動不動就手寫各種源碼妄荔。當然手寫源碼不是目的泼菌,可能就是為了考察對知識的掌握程度。很多人一聽到手寫Promise
就頭大啦租,毫無思路灶轰,其實我們把Promise
功能需求拆封一下,分步驟實現刷钢;寫起來就相對簡單了笋颤,并且易于理解,下面就帶大家手寫一個Promise
。
:sunny:閱讀小提示:本文看起來篇幅很長伴澄,因為每部分都貼出了完整代碼赋除,所以代碼量比較大。每個步驟新增/修改
的代碼部分都會標明步驟和思路非凌,跟著步驟閱讀即可举农。耐心看完,相信會有收獲敞嗡!
Promise基本邏輯實現
- 先貼代碼颁糟,分析一下:
new Promise((resolve,reject)=>{
console.log('LonJIn帶你手寫Promise');
resolve('hello')
}).then((res)=>{
console.log(res)
})
核心邏輯分析
首先,我們看到
Promise
前面調用了new
喉悴,說明Promise
是一個類它有三種狀態(tài):
等待(pending)
棱貌、成功(fulfilled)
、失敗(rejected)
箕肃,狀態(tài)一旦從pending
變成fulfilled
或rejected
就不可更改在
new Promise
中傳入了一個函數婚脱,我們可以把它稱之為執(zhí)行器,執(zhí)行器會立即執(zhí)行在執(zhí)行器中傳入了
resolve
和reject
,用來改變它的狀態(tài)then
方法內部做的事情就是判斷狀態(tài)勺像,如果狀態(tài)是成功障贸,調用成功回調函數,如果狀態(tài)是失敗,就調用失敗回調函數,且then
方法會接收一個成功或者失敗的值吟宦。
代碼實現
- 我們先建立一個
MyPromise.js
文件篮洁,在這個文件中編寫我們的Promise。
//定義三個常量
const PENDING='pending', //等待
FULFILLED='fulfilled', //成功
REJECTED='rejected'; //失敗
class MyPromise{
constructor(exector){
// 立即執(zhí)行函數殃姓,傳入resolve方法和reject方法
exector(this.resolve,this.reject)
};
//定義一個初始狀態(tài)
status=PENDING;
//保存成功后的值
value=undefined;
//保存失敗后的值
reason=undefined;
//成功
resolve=value=>{
//判斷當前狀態(tài)是否為PENDING嘀粱,如果不是就return
if(this.status!==PENDING)return;
//更改狀態(tài)為fulfilled
this.status=FULFILLED;
//保存成功的返回值
this.value=value;
};
//失敗
reject=reason=>{
//判斷當前狀態(tài)是否為PENDING,如果不是就return
if(this.status!==PENDING)return;
//更改狀態(tài)為rejected
this.status=REJECTED;
//保存失敗的返回值
this.reason=reason;
};
//then方法會接收兩個回調函數辰狡,分別為成功和失敗的回調函數
then(successCallback,failCallback){
//判斷當前狀態(tài)锋叨,然后調用相應的函數
if(this.status===FULFILLED){
//傳入返回值
successCallback(this.value)
}else if(this.status===REJECTED){
//傳入返回值
failCallback(this.reason)
}
}
}
//導出MyPromise
module.exports = MyPromise
- 我們上面分析的基本完成,新建一個
index.js
導入測試一下:
//index.js
const MyPromise=require('./MyPromise');
let n=new MyPromise((resolve,reject)=>{
console.log('lonjin');
resolve(200)
// reject('erro')
})
n.then((val)=>{
console.log(val)
},(erro)=>{
console.log(erro)
})
//lonjin
//200
- 調用之后可以正常輸出宛篇,說明之前寫的沒有問題娃磺,但還有個問題:我們沒有考慮異步的時候該怎么辦?比如下面的代碼:
//index.js
const MyPromise=require('./MyPromise');
let n=new MyPromise((resolve,reject)=>{
//由于setTimeout為異步代碼叫倍,then會馬上執(zhí)行偷卧,但此時狀態(tài)還為pending之前并沒有判斷等待這個狀態(tài)
setTimeout(() => {
resolve(200)
}, 100);
})
n.then((val)=>{
console.log(val)
},(erro)=>{
console.log(erro)
})
處理異步調用
- 由于執(zhí)行器可能有異步代碼,所以我們在
then
方法中還需要判斷一下狀態(tài)吆倦,然后做相應的處理
代碼如下(為了思路清晰听诸,只對做更改的地方加注釋,會標注實現步驟蚕泽,按步驟閱讀即可):
const PENDING='pending',
FULFILLED='fulfilled',
REJECTED='rejected';
class MyPromise{
constructor(exector){
exector(this.resolve,this.reject)
};
status=PENDING;
value=undefined;
reason=undefined;
//定義一個成功回調函數
successCallback=undefined;
//定義一個失敗回調函數
failCallback=undefined;
resolve=value=>{
if(this.status!==PENDING)return;
this.status=FULFILLED;
this.value=value;
/* 步驟2
-------------------------------------------------
判斷成功回調是否存在晌梨,如果存在桥嗤,直接調用
*/
this.successCallback && this.successCallback(this.value)
};
reject=reason=>{
if(this.status!==PENDING)return;
this.status=REJECTED;
this.reason=reason;
/* 步驟3
-------------------------------------------------
判斷失敗回調是否存在,如果存在仔蝌,直接調用
*/
this.failCallback && this.failCallback(this.reason)
};
then(successCallback,failCallback){
if(this.status===FULFILLED){
successCallback(this.value)
}else if(this.status===REJECTED){
failCallback(this.reason)
}else{
/* 步驟1
-------------------------------------------------
等待狀態(tài)泛领,處理異步邏輯
由于不知道狀態(tài),所以我們需要把成功和失敗的回調函數先保存起來
保存回調函數 */
this.successCallback=successCallback;
this.failCallback=failCallback;
}
}
}
//導出MyPromise
module.exports = MyPromise
- 驗證代碼:
//index.js
const MyPromise=require('./MyPromise');
let n=new MyPromise((resolve,reject)=>{
//由于setTimeout為異步代碼敛惊,then會馬上執(zhí)行渊鞋,但此時狀態(tài)還為pending之前并沒有判斷等待這個狀態(tài)
setTimeout(() => {
resolve(200)
}, 100);
})
n.then((val)=>{
console.log(val)
},(erro)=>{
console.log(erro)
})
- 我們可以看到代碼可以正常輸出,異步就算處理完成瞧挤。
實現then()多次調用
-
Promise
的then
方法是可以被多次調用的锡宋。如果是同步回調,那么直接返回當前的值就行特恬;如果是異步回調执俩,那么保存的成功失敗的回調,需要用不同的值保存鸵鸥,因為都互不相同奠滑。所以之前的代碼需要改造一下丹皱。
//index.js
const MyPromise=require('./MyPromise');
let n=new MyPromise((resolve,reject)=>{
setTimeout(() => {
resolve(200)
}, 100);
})
//多次調用的情況 目前只會輸出一次
n.then((val)=>{
console.log(val)
},(erro)=>{
console.log(erro)
})
n.then((val)=>{
console.log(val)
},(erro)=>{
console.log(erro)
})
n.then((val)=>{
console.log(val)
},(erro)=>{
console.log(erro)
})
代碼實現
- 為了解決多次調用的情況妒穴,之前我們定義了一個
successCallback
回調函數,默認為undefined
,這時候我們需要把它改成數組形式摊崭,在resolve
或reject
回調函數中取數組最后一個保存的回調執(zhí)行即可
const PENDING='pending',
FULFILLED='fulfilled',
REJECTED='rejected';
class MyPromise{
constructor(exector){
exector(this.resolve,this.reject)
};
status=PENDING;
value=undefined;
reason=undefined;
/* 步驟1
-------------------------------------
將successCallback和failCallback改成數組
*/
successCallback=[];
failCallback=[];
resolve=value=>{
if(this.status!==PENDING)return;
this.status=FULFILLED;
this.value=value;
/* 步驟3
-------------------------------------
判斷successCallback數組中是否有值讼油,有值的話就取數組最后的執(zhí)行
*/
while(this.successCallback.length)this.successCallback.shift()(this.value)
};
reject=reason=>{
if(this.status!==PENDING)return;
this.status=REJECTED;
this.reason=reason;
/* 步驟4
-------------------------------------
判斷failCallback數組中是否有值,有值的話就取數組最后的執(zhí)行
*/
while(this.failCallback.length)this.failCallback && this.failCallback.shift()(this.reason)
};
then(successCallback,failCallback){
if(this.status===FULFILLED){
successCallback(this.value)
}else if(this.status===REJECTED){
failCallback(this.reason)
}else{
/* 步驟2
-------------------------------------
將successCallback和failCallback函數push到數組中
*/
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
}
}
module.exports = MyPromise
- 驗證代碼:
//index.js
const MyPromise=require('./MyPromise');
let n=new MyPromise((resolve,reject)=>{
setTimeout(() => {
resolve(200)
}, 500);
})
n.then((val)=>{
console.log('one-'+val)
},(erro)=>{
console.log(erro)
})
n.then((val)=>{
console.log('two-'+val)
},(erro)=>{
console.log(erro)
})
n.then((val)=>{
console.log('three-'+val)
},(erro)=>{
console.log(erro)
})
/*
one-200
two-200
three-200
*/
代碼正常輸出呢簸,證明無問題矮台。
實現then方法的鏈式調用
還是先理一下需求,再進行開發(fā):
-
then
方法的鏈式調用會返回一個Promise
根时,且then
方法的return
值會作為參數傳給下一個then
代碼實現
const PENDING='pending',
FULFILLED='fulfilled',
REJECTED='rejected';
class MyPromise{
constructor(exector){
exector(this.resolve,this.reject)
};
status=PENDING;
value=undefined;
reason=undefined;
successCallback=[];
failCallback=[];
resolve=value=>{
if(this.status!==PENDING)return;
this.status=FULFILLED;
this.value=value;
while(this.successCallback.length)this.successCallback.shift()(this.value)
};
reject=reason=>{
if(this.status!==PENDING)return;
this.status=REJECTED;
this.reason=reason;
while(this.failCallback.length)this.failCallback && this.failCallback.shift()(this.reason)
};
then(successCallback,failCallback){
/* 步驟1
------------------------
1.then方法會返回一個Promise,所以這里需要創(chuàng)建一個Promise瘦赫,然后return回去
2. 我們先把 成功回調函數保存為n
3.然后在MyPromise類的外部再寫一個resolvePromise做處理
*/
let prmoise2=new MyPromise((resolve,reject)=>{
if(this.status===FULFILLED){
//保存成功回調函數
let n=successCallback(this.value)
//傳入resolvePromise函數中去做判斷
resolvePromise(n,resolve,reject)
}else if(this.status===REJECTED){
failCallback(this.reason)
}else{
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
return prmoise2;
}
}
/* 步驟2
---------------------------
//判斷x是不是其實例的對象
*/
function resolvePromise(n,resolve,reject){
// 判斷x是不是其實例對象,如果是就直接調用then方法
if(n instanceof MyPromise){
n.then(resolve,reject)
}else{
//普通值蛤迎,直接調用resolve
resolve(n)
}
}
module.exports = MyPromise
:rocket:驗證代碼
const MyPromise=require('./MyPromise');
let n=new MyPromise((resolve,reject)=>{
resolve('lonjin')
})
function m(){
return new MyPromise((resolve,reject)=>{
console.log('------')
resolve('hello')
})
}
n.then((res)=>{
console.log(res)
return m();
})
.then((res)=>{
console.log(res)
})
/*
lonjin
------
hello
*/
:warning:特殊情況處理
:one: 如果then
方法返回的是自己的promise
對象确虱,則會發(fā)生promise
的嵌套,這個時候程序會報錯,如下代碼:
var promise = new Promise((resolve, reject) => {
resolve(100)
})
var n = promise.then(value => {
console.log(value)
return n
})
// 100
// Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
:two: 同時還要處理一種情況替裆,如果我們在new MyPromise
中傳入的立即執(zhí)行器中代碼報錯校辩,以及then()
中寫的代碼報錯,我們都需要捕獲到辆童,然后執(zhí)行reject
:three: 之前一直是處理resolve
的情況宜咒,還需要處理一下reject
的情況
這時候我們需要改造一下剛才寫的代碼:
const PENDING='pending',
FULFILLED='fulfilled',
REJECTED='rejected';
class MyPromise{
/* 步驟1
-------------------
捕獲一下立即執(zhí)行器中的錯誤
*/
constructor(exector){
try{
exector(this.resolve,this.reject)
}catch(e){
reject(e)
}
};
status=PENDING;
value=undefined;
reason=undefined;
successCallback=[];
failCallback=[];
resolve=value=>{
if(this.status!==PENDING)return;
this.status=FULFILLED;
this.value=value;
/* 步驟6
-------------
由于修改了下面的successCallback,所以不需要傳遞this.value
*/
while(this.successCallback.length)this.successCallback.shift()()
};
reject=reason=>{
if(this.status!==PENDING)return;
this.status=REJECTED;
this.reason=reason;
/* 步驟7
-------------
由于修改了下面的failCallback把鉴,所以不需要傳遞this.reason
*/
while(this.failCallback.length)this.failCallback && this.failCallback.shift()()
};
then(successCallback,failCallback){
let prmoise2=new MyPromise((resolve,reject)=>{
if(this.status===FULFILLED){
/* 步驟2
---------------------
因為new Promise需要執(zhí)行完成之后才有promise2故黑,同步代碼中沒有pormise2,
所以這部分代碼需要異步執(zhí)行 ,我們把prmoise2傳入resolvePromise函數中
去判斷一下prmoise2是否等于n倍阐,同時需要捕獲一下錯誤
*/
setTimeout(() => {
try{
let n=successCallback(this.value)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
}else if(this.status===REJECTED){
/*
步驟3
---------------------
處理一下失敗的情況
*/
setTimeout(() => {
try{
let n= failCallback(this.reason)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
}else{
/*
步驟5
---------------------
修改一下異步情況
*/
this.successCallback.push(()=>{
setTimeout(() => {
try{
let n=successCallback(this.value)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
});
this.failCallback.push(()=>{
setTimeout(() => {
try{
let n= failCallback(this.reason)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
});
}
});
return prmoise2;
}
}
function resolvePromise(prmoise2,n,resolve,reject){
/* 步驟4
----------------
如果n和prmoise2相等 則返回錯誤提示
*/
if(prmoise2===n){
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// 判斷x是不是其實例對象
if(n instanceof MyPromise){
n.then(resolve,reject)
}else{
//普通值概疆,直接調用resolve
resolve(n)
}
}
//導出MyPromise
module.exports = MyPromise
:rocket:驗證代碼
//驗證1
//index.js
const MyPromise = require('./myPromise')
var promise = new MyPromise((resolve, reject) => {
resolve(100)
})
var p1 = promise.then(value => {
console.log(value)
return p1
})
p1.then((val)=>{
console.log(val)
},(erro)=>{
console.log(erro.message)
})
// 100
// Chaining cycle detected for promise #<Promise>
//驗證2
var errofn = new MyPromise((resolve, reject) => {
throw new Error('執(zhí)行器拋出異常!')
})
errofn.then((val)=>{
console.log(val)
},(erro)=>{
console.log(erro)
})
// 執(zhí)行器錯誤
將then方法的參數變成可選參數
- 我們知道峰搪,調用
then()
時候也可以不傳遞任何參數岔冀,如下面這種情況
let promise =new Promise((resolve,reject)=>{
resolve('lonjin')
})
promise.then().then().then((value)=>{
console.log(value)
})
//lonjin
- 相當于這樣:
promise
.then(value => value)
.then(value => value)
.then(value => value)
.then(value => console.log(value))
所以我們要把then
方法的參數改為可選參數,如果有參數就傳入概耻,如果沒有返回value
:
代碼實現
const PENDING='pending',
FULFILLED='fulfilled',
REJECTED='rejected';
class MyPromise{
constructor(exector){
try{
exector(this.resolve,this.reject)
}catch(e){
reject(e)
}
};
status=PENDING;
value=undefined;
reason=undefined;
successCallback=[];
failCallback=[];
resolve=value=>{
if(this.status!==PENDING)return;
this.status=FULFILLED;
this.value=value;
while(this.successCallback.length)this.successCallback.shift()()
};
reject=reason=>{
if(this.status!==PENDING)return;
this.status=REJECTED;
this.reason=reason;
while(this.failCallback.length)this.failCallback && this.failCallback.shift()()
};
then(successCallback,failCallback){
/*步驟1
----------------
判斷參數是否存在
*/
successCallback=successCallback?successCallback:value=>value;
failCallback=failCallback?failCallback:reason=>{throw reason};
let prmoise2=new MyPromise((resolve,reject)=>{
if(this.status===FULFILLED){
setTimeout(() => {
try{
let n=successCallback(this.value)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
}else if(this.status===REJECTED){
setTimeout(() => {
try{
let n= failCallback(this.reason)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
}else{
this.successCallback.push(()=>{
setTimeout(() => {
try{
let n=successCallback(this.value)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
});
this.failCallback.push(()=>{
setTimeout(() => {
try{
let n= failCallback(this.reason)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
});
}
});
return prmoise2;
}
}
function resolvePromise(prmoise2,n,resolve,reject){
if(prmoise2===n){
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if(n instanceof MyPromise){
n.then(resolve,reject)
}else{
resolve(n)
}
}
module.exports = MyPromise
:rocket:驗證代碼
const MyPromise = require('./myPromise')
let promise =new MyPromise((resolve,reject)=>{
resolve('lonjin')
})
promise.then().then().then((value)=>{
console.log(value)
})
//lonjin
Promise.all()方法實現
還是先分析一下all
方法:
function fn1(){
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('fn1')
}, 3000);
})
}
function fn2(){
return new Promise((resolve,reject)=>{
resolve('fn2')
})
}
Promise.all(['a','b',fn1(),fn2(),'e'])
.then((res)=>{
console.log(res)
},(erro)=>{
console.log(erro)
})
//[ 'a', 'b', 'fn1', 'fn2', 'e' ]
all
方法接收一個數組使套,其內部可以傳入普通值
和Promise
對象all
方法返回的也是一個Promise
對象,其返回值為一個數組all
方法中傳入的Promise
對象鞠柄,如果都是成功侦高,返回為成功,如果有一個失敗厌杜,就會走失敗的回調函數
分析完代碼奉呛,我們就可以進行編寫了,具體代碼如下:
代碼實現
const PENDING='pending',
FULFILLED='fulfilled',
REJECTED='rejected';
class MyPromise{
constructor(exector){
try{
exector(this.resolve,this.reject)
}catch(e){
reject(e)
}
};
status=PENDING;
value=undefined;
reason=undefined;
successCallback=[];
failCallback=[];
resolve=value=>{
if(this.status!==PENDING)return;
this.status=FULFILLED;
this.value=value;
while(this.successCallback.length)this.successCallback.shift()()
};
reject=reason=>{
if(this.status!==PENDING)return;
this.status=REJECTED;
this.reason=reason;
while(this.failCallback.length)this.failCallback && this.failCallback.shift()()
};
then(successCallback,failCallback){
successCallback=successCallback?successCallback:value=>value;
failCallback=failCallback?failCallback:reason=>{throw reason};
let prmoise2=new MyPromise((resolve,reject)=>{
if(this.status===FULFILLED){
setTimeout(() => {
try{
let n=successCallback(this.value)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
}else if(this.status===REJECTED){
setTimeout(() => {
try{
let n= failCallback(this.reason)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
}else{
this.successCallback.push(()=>{
setTimeout(() => {
try{
let n=successCallback(this.value)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
});
this.failCallback.push(()=>{
setTimeout(() => {
try{
let n= failCallback(this.reason)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
});
}
});
return prmoise2;
};
/*
all()實現all方法
*/
static all(array){
// 要返回的數組
let result=[];
//記錄執(zhí)行次數
let index=0;
//all方法返回的也是一個promise
return new MyPromise((resolve,reject)=>{
//定義一個存到result數組中的方法
function addItem(key,value){
result[key]=value;
index++;
//如果index等于傳入array的長度夯尽,說明執(zhí)行完成
if(index==array.length){
resolve(result)
}
}
//循環(huán)array
for(let i=0;i<array.length;i++){
//當前的參數
let current=array[i];
//判斷一下當前的返回值是普通值還是promise
if(current instanceof MyPromise){
current.then(value=>addItem(i,value),reason=>reject(reason));
}else{
addItem(i,current)
}
}
})
}
}
function resolvePromise(prmoise2,n,resolve,reject){
if(prmoise2===n){
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if(n instanceof MyPromise){
n.then(resolve,reject)
}else{
resolve(n)
}
}
module.exports = MyPromise
Promise.resolve()方法實現
resolve
方法可以接受一個prmoise
對象瞧壮,也可以接收一個普通值,如果是普通值需要包裝成一個prmoise
返回匙握。
代碼實現
const PENDING='pending',
FULFILLED='fulfilled',
REJECTED='rejected';
class MyPromise{
constructor(exector){
try{
exector(this.resolve,this.reject)
}catch(e){
reject(e)
}
};
status=PENDING;
value=undefined;
reason=undefined;
successCallback=[];
failCallback=[];
resolve=value=>{
if(this.status!==PENDING)return;
this.status=FULFILLED;
this.value=value;
while(this.successCallback.length)this.successCallback.shift()()
};
reject=reason=>{
if(this.status!==PENDING)return;
this.status=REJECTED;
this.reason=reason;
while(this.failCallback.length)this.failCallback && this.failCallback.shift()()
};
then(successCallback,failCallback){
successCallback=successCallback?successCallback:value=>value;
failCallback=failCallback?failCallback:reason=>{throw reason};
let prmoise2=new MyPromise((resolve,reject)=>{
if(this.status===FULFILLED){
setTimeout(() => {
try{
let n=successCallback(this.value)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
}else if(this.status===REJECTED){
setTimeout(() => {
try{
let n= failCallback(this.reason)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
}else{
this.successCallback.push(()=>{
setTimeout(() => {
try{
let n=successCallback(this.value)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
});
this.failCallback.push(()=>{
setTimeout(() => {
try{
let n= failCallback(this.reason)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
});
}
});
return prmoise2;
};
static all(array){
let result=[];
let index=0;
return new MyPromise((resolve,reject)=>{
function addItem(key,value){
result[key]=value;
index++;
if(index==array.length){
resolve(result)
}
}
for(let i=0;i<array.length;i++){
let current=array[i];
if(current instanceof MyPromise){
current.then(value=>addItem(i,value),reason=>reject(reason));
}else{
addItem(i,current)
}
}
})
};
/*
resolve方法
*/
static resolve(value){
//判斷value 是否為MyPromise的實例咆槽,如果是,直接返回
if( value instanceof MyPromise) return value;
//如果不是圈纺,return一個prmoise
return new MyPromise(resolve=>resolve(value))
}
}
function resolvePromise(prmoise2,n,resolve,reject){
if(prmoise2===n){
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if(n instanceof MyPromise){
n.then(resolve,reject)
}else{
resolve(n)
}
}
module.exports = MyPromise
代碼驗證
const MyPromise = require('./myPromise');
MyPromise.resolve('lonjin').then((res)=>{
console.log(res)
});
//lonjin
finally()方法實現
finally()
方法返回一個Promise
秦忿。在Promise
結束時,無論結果是fulfilled
或者是rejected
蛾娶,都會執(zhí)行指定的回調函數灯谣。這為在Promise
是否成功完成后都需要執(zhí)行的代碼提供了一種方式。
這避免了同樣的語句需要在then()
和catch()
中各寫一次的情況蛔琅。
- 返回的是
Promise
- 無論成功和失敗都會執(zhí)行
代碼實現
//MyPromise中
finally(callback){
// 使用then方法拿到當前的promise的狀態(tài)
return this.then(value=>{
/* 如果callback是一個異步的promise對象胎许,我們還需要等待其執(zhí)行完畢,所以需要用到靜態(tài)方法resolve
把callback調用之后返回的promise傳遞過去揍愁,并且執(zhí)行promise呐萨,且在成功之后返回value
*/
return MyPromise.resolve(callback()).then(()=>value)
},reason=>{
// 失敗之后調用的then方法,然后把失敗的原因返回出去
return MyPromise.resolve(callback()).then(() => { throw reason })
})
}
代碼驗證
const MyPromise = require('./myPromise')
function fn1(){
return new MyPromise((resolve,reject)=>{
setTimeout(() => {
reject('100')
}, 3000);
})
}
fn1().finally(()=>{
console.log('finally')
}).then((value)=>{
console.log(value)
},(erro)=>{
console.log(erro)
})
//finally
// 100
catch方法的實現
- catch方法是為了捕獲promise對象的所有錯誤回調的
- 直接調用then方法莽囤,然后成功的地方傳遞undefined谬擦,錯誤的地方傳遞reason
- catch方法是作用在原型對象上的方法
代碼實現
catch (failCallback) {
// 直接調用then方法,然后成功的地方傳遞undefined朽缎,錯誤的地方傳遞reason
return this.then(undefined, failCallback)
}
完整代碼
完整代碼在下方惨远,所有的都加了注釋谜悟,需要的直接復制到編輯器看吧!
//定義三個常量
const PENDING='pending', //等待
FULFILLED='fulfilled', //成功
REJECTED='rejected'; //失敗
class MyPromise{
constructor(exector){
// 立即執(zhí)行函數北秽,傳入resolve方法和reject方法
//同時捕獲一下錯誤
try{
exector(this.resolve,this.reject)
}catch(e){
this.reject(e)
}
};
//定義一個初始狀態(tài)
status=PENDING;
//保存成功后的值
value=undefined;
//保存失敗后的值
reason=undefined;
//定義一個成功回調函數
successCallback=[];
//定義一個失敗回調函數
failCallback=[];
//成功
resolve=value=>{
//判斷當前狀態(tài)是否為PENDING葡幸,如果不是就return
if(this.status!==PENDING)return;
//更改狀態(tài)為fulfilled
this.status=FULFILLED;
//保存成功的返回值
this.value=value;
//判斷成功回調是否存在,如果存在贺氓,直接調用
while(this.successCallback.length)this.successCallback.shift()()
};
//失敗
reject=reason=>{
//判斷當前狀態(tài)是否為PENDING蔚叨,如果不是就return
if(this.status!==PENDING)return;
//更改狀態(tài)為rejected
this.status=REJECTED;
//保存失敗的返回值
this.reason=reason;
//判斷成功回調是否存在,如果存在辙培,直接調用
while(this.failCallback.length)this.failCallback && this.failCallback.shift()()
};
//then方法會接收兩個回調函數蔑水,分別為成功和失敗的回調函數
then(successCallback,failCallback){
//處理then方法可選參數
successCallback=successCallback?successCallback:value=>value;
failCallback=failCallback?failCallback:reason=>{throw reason}
let prmoise2=new MyPromise((resolve,reject)=>{
//判斷當前狀態(tài),然后調用相應的函數
if(this.status===FULFILLED){ //成功狀態(tài)
//傳入返回值
/*因為new Promise需要執(zhí)行完成之后才有promise2扬蕊,同步代碼中沒有pormise2搀别,
所以這部分代碼需要異步執(zhí)行 */
setTimeout(() => {
//捕獲錯誤
try{
let n=successCallback(this.value)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
}else if(this.status===REJECTED){ //失敗狀態(tài)
//傳入返回值
setTimeout(() => {
//捕獲錯誤
try{
let n= failCallback(this.reason)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
}else{ //等待狀態(tài)
//保存回調函數(考慮到異步情況)
this.successCallback.push(()=>{
setTimeout(() => {
//捕獲錯誤
try{
let n=successCallback(this.value)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
});
this.failCallback.push(()=>{
setTimeout(() => {
//捕獲錯誤
try{
let n= failCallback(this.reason)
resolvePromise(prmoise2,n,resolve,reject)
}catch(e){
reject(e)
}
}, 0);
});
}
});
return prmoise2;
};
// 直接調用then方法,然后成功的地方傳遞undefined尾抑,錯誤的地方傳遞reason
catch (failCallback) {
return this.then(undefined, failCallback)
}
finally(callback){
// 使用then方法拿到當前的promise的狀態(tài)
return this.then(value=>{
/* 如果callback是一個異步的promise對象歇父,我們還需要等待其執(zhí)行完畢,所以需要用到靜態(tài)方法resolve
把callback調用之后返回的promise傳遞過去再愈,并且執(zhí)行promise榜苫,且在成功之后返回value
*/
return MyPromise.resolve(callback()).then(()=>value)
},reason=>{
// 失敗之后調用的then方法,然后把失敗的原因返回出去
return MyPromise.resolve(callback()).then(() => { throw reason })
})
}
static all(array){
// 要返回的數組
let result=[];
//記錄執(zhí)行次數
let index=0;
//all方法返回的也是一個promise
return new MyPromise((resolve,reject)=>{
//定義一個存到result數組中的方法
function addItem(key,value){
result[key]=value;
index++;
if(index==array.length){
resolve(result)
}
}
//循環(huán)array
for(let i=0;i<array.length;i++){
//當前的參數
let current=array[i];
//判斷一下當前的返回值是普通值還是promise
if(current instanceof MyPromise){
current.then(value=>addItem(i,value),reason=>reject(reason));
}else{
addItem(i,current)
}
}
})
};
static resolve(value){
//判斷value 是否為MyPromise的實例践磅,如果是单刁,直接返回
if( value instanceof MyPromise) return value;
//如果不是灸异,return一個prmoise
return new MyPromise(resolve=>resolve(value))
}
}
//判斷x是不是其實例的對象
function resolvePromise(prmoise2,n,resolve,reject){
//如果n和prmoise2相等 則返回錯誤提示
if(prmoise2===n){
console.log('相等')
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// 判斷x是不是其實例對象
if(n instanceof MyPromise){
n.then(resolve,reject)
}else{
//普通值府适,直接調用resolve
resolve(n)
}
}
//導出MyPromise
module.exports = MyPromise