一饲宛、Promise
1.異步任務(wù)的處理
在ES6出來之后扮匠,有很多關(guān)于Promise的講解、文章芙贫,也有很多經(jīng)典的書籍講解Promise.
- 雖然等你學(xué)會了Promise之后搂鲫,會覺得Promise不過如此傍药,但是在初次接觸的時候都會覺得這個東西不好理解。
那么這里我從幾個實際的例子來作為切入點:
- 我們調(diào)用一個函數(shù)魂仍,這個函數(shù)中發(fā)送網(wǎng)絡(luò)請求(我們可以用定時器來模擬)
- 如果發(fā)送網(wǎng)絡(luò)請求成功了拐辽,那么告知調(diào)用者發(fā)送成功,并且將相關(guān)數(shù)據(jù)返回過去擦酌。
- 如果發(fā)送網(wǎng)絡(luò)請求失敗了俱诸,那么告知調(diào)用者發(fā)送失敗,并且告知錯誤信息赊舶。
【任何一個新技術(shù)的出來睁搭,就是用來解決原有技術(shù)的某一個痛點】
//request.js
function requestData(url){
// 模擬網(wǎng)絡(luò)請求
setTimeout(()=>{
//拿到請求的1結(jié)果
//url傳入的是 coderwhy,請求成功
if(url==="coderwhy"){
let names=["wjy","hyz","tqy"];
return names;//* 但其實這個return外面是拿不到結(jié)果的
}else{// 否則請求失敗
let errMessage="請求失敗,url錯誤";
return errMessage;//* 但其實這個return外面是拿不到結(jié)果的
}
},3000)
}
// main.js
requestData("coderwhy")
修改requestData參數(shù)笼平,需要傳入成功的回調(diào)函數(shù)successCallback和失敗的回調(diào)函數(shù)failCallback园骆。
//request.js
function requestData(url,successCallback,failCallback){
// 模擬網(wǎng)絡(luò)請求
setTimeout(()=>{
//拿到請求的1結(jié)果
//url傳入的是 coderwhy,請求成功
if(url==="coderwhy"){
let names=["wjy","hyz","tqy"];
successCallback(names);
}else{// 否則請求失敗
let errMessage="請求失敗,url錯誤";
failCallback(errMessage)
}
},3000)
}
// main.js
requestData("coderwhy",(res)=>{
console.log("成功:",res);
},(err)=>{
console.log("失斣⒌鳌:",err);
})
1.1 回調(diào)函數(shù)的弊端
- 如果是我們自己封裝的requestData锌唾,那么我們在封裝的時候必須要自己設(shè)計好callback名稱,并且使用好
- 如果我們使用的是別人封裝的requestData或者一些第三方庫夺英,那么我們必須去看別人的源碼或者文檔晌涕,才知道這個函數(shù)怎么去獲取結(jié)果的。
- 這種溝通成本非常多的
那么有沒有一種更好的方式:
- 那么有沒有會返回一個承諾痛悯,這個承諾(規(guī)范了所有的代碼編寫邏輯)
// * 更好的方案 承諾(規(guī)范好了所有的代碼編寫邏輯)
function requestData2(){
return "承諾"
}
const chengnuo=requestData2()
2.什么是Promise
Promise是ES6新增的一個API余黎,它是一個類,可以翻譯成 承諾载萌、許諾惧财、期約亲族;
當(dāng)我們需要給予調(diào)用者一個承諾:待會兒我會傳給你回調(diào)數(shù)據(jù)時,就可以創(chuàng)建一個Promise對象
在通過new 創(chuàng)建Promise對象時可缚,我們需要傳入一個回調(diào)函數(shù)霎迫,我們稱之為executor
- 這個回調(diào)函數(shù)會被立即執(zhí)行,并且給傳入另外兩個回調(diào)函數(shù)resolve帘靡、reject
- 當(dāng)我們調(diào)用resolve回調(diào)函數(shù)時知给,會執(zhí)行Promise對象的then方法傳入的回調(diào)函數(shù)
- 當(dāng)我們調(diào)用reject回調(diào)函數(shù)時,會執(zhí)行Promise對象的catch方法傳入的回調(diào)函數(shù)描姚。
function foo(){
// Promise
return new Promise((resolve,reject)=>{
// resolve();
reject();
})
}
// main.js
const fooPromise=foo();
// * then方法傳入的回調(diào)函數(shù)涩赢,會在Promise執(zhí)行resolve函數(shù)時,會回調(diào)轩勘。
fooPromise.then(()=>{
})
// * catch方法傳入的回調(diào)函數(shù)筒扒,會在Promise執(zhí)行reject函數(shù)時,被回調(diào)
fooPromise.catch(()=>{
})
// class Person{
// constructor(callback){
// function foo(){
// }
// function bar(){
// }
// callback(foo,bar)
// }
// }
// const p=new Person((foo,bar)=>{
// foo();
// bar()
// });
// * Promise是一個類绊寻,可以傳入?yún)?shù)花墩,參數(shù)是回調(diào)函數(shù),這個回調(diào)函數(shù)在傳入時澄步,會立即被執(zhí)行冰蘑。
/**
* * 傳入的回調(diào)函數(shù)可以稱之為 executor
* * 這個回調(diào)函數(shù)會有兩個參數(shù):
* * resolve :成功時,執(zhí)行resolve回調(diào)
* * reject:失敗時村缸,執(zhí)行reject回調(diào)
*/
const promise=new Promise((resolve,reject)=>{
// console.log("promise傳入的函數(shù)立即被執(zhí)行了");
resolve();
})
promise.then(()=>{
})
promise.catch(()=>{
})
2.1 異步處理的Promise方式
/**
* * 回調(diào)的弊端
* * 1.如果是我們自己封裝的requestData祠肥,那么我們在封裝的時候必須要自己設(shè)計好callback名稱,并且使用好
* * 2.如果我們使用的是別人封裝的requestData或者一些第三方庫梯皿,那么我們必須去看別人的源碼或者文檔仇箱,才知道這個函數(shù)怎么去獲取結(jié)果的。
* * 3.這種溝通成本非常多的
*/
//request.js
function requestData(url,successCallback,failCallback){
// 模擬網(wǎng)絡(luò)請求
return new Promise((resolve,reject)=>{
setTimeout(()=>{
//拿到請求的1結(jié)果
//url傳入的是 coderwhy东羹,請求成功
if(url==="coderwhy"){
let names=["wjy","hyz","tqy"];
resolve(names);
}else{// 否則請求失敗
let errMessage="請求失敗,url錯誤";
reject(errMessage)
}
},3000);
})
}
//main.js
const promise=requestData("coderwhy");
// * then方法傳入兩個回調(diào)函數(shù)
// * 第一個回調(diào)函數(shù)剂桥,會在Promise執(zhí)行resolve函數(shù)時,被回調(diào)
// * 第二個回調(diào)函數(shù)百姓,會在Promise執(zhí)行reject函數(shù)時渊额,被回調(diào)
promise.then((res)=>{
console.log("請求成功:",res);
},(err)=>{
console.log("請求失敗了:",err);
})
3.Promise的三種狀態(tài)
// 完全等價于下面的代碼
new Promise((resolve,reject)=>{
console.log("------------------");//* 階段:pending
resolve();
}).then(res=>{
console.log("res:",res);//* fulfilled(固定、已敲定)
},err=>{
console.log("err:",err);//* rejected (已拒絕 )
})
上面的Promise使用過程垒拢,我們可以將它劃分成三個狀態(tài):
-
待定(pending):初始狀態(tài)旬迹,既沒有被兌現(xiàn),也沒有被拒絕
- 當(dāng)執(zhí)行executor中的代碼時求类,處于該狀態(tài)奔垦。
-
已兌現(xiàn)(fulfilled):意味著操作成功完成
- 執(zhí)行了resolved時,處于該狀態(tài)
-
已拒絕(rejected):意味著操作失敗
- 執(zhí)行了reject 時尸疆,處于該狀態(tài)
Promise狀態(tài)一旦被確定下來椿猎,那么就是不可更改的(鎖定)惶岭,例如下面我們先執(zhí)行了resolve,將狀態(tài)改為 已兌現(xiàn)(fulfilled)犯眠。后面又執(zhí)行了reject按灶,企圖將狀態(tài)修改為已拒絕(rejected),但發(fā)現(xiàn)其實并不能進(jìn)行修改的筐咧。
// * Promise狀態(tài)一旦被確定下來鸯旁,那么就是不可更改的(鎖定)
new Promise((resolve,reject)=>{
console.log("------------------");//* 階段:pending
resolve();//* fulfilled(固定、已敲定)
reject();////* rejected (已拒絕 ) 量蕊,這行代碼沒有任何意義铺罢。
// resolve(123)
}).then(res=>{
console.log("res:",res);
},err=>{
console.log("err:",err);
})
4. resolve參數(shù)
- 情況一:如果resolve傳入一個普通的值或者對象,那么這個值就會作為then回調(diào)的參數(shù)
- 情況二:如果resolve傳入的是另外一個Promise残炮,那么這個新Promise的狀態(tài)會決定原Promise的狀態(tài)
- 情況三:如果resolve傳入的是一個對象韭赘,并且這個對象有實現(xiàn)then方法,那么就會執(zhí)行then方法势就,并且根據(jù)then方法的結(jié)果來決定Promise的狀態(tài)泉瞻。
/**
* resolve(參數(shù))
* * (1)普通的值或者對象
* * (2)傳入一個Promise
* * 那么當(dāng)前的Promise狀態(tài)由傳入的Promise來決定
* * 相當(dāng)于狀態(tài)進(jìn)行了移交
* * (3)傳入一個對象,并且這個對象有實現(xiàn)的then方法 (thenable)蛋勺,那么也會執(zhí)行該then方法瓦灶,并且由該then方法決定后續(xù)狀態(tài)
*/
// * 傳入Promise的特殊情況
// const newPromise=new Promise((resolve,reject)=>{
// // resolve("aaaaa")
// reject("err message")
// })
// new Promise((resolve,reject)=>{
// resolve(newPromise)
// }).then(res=>{
// console.log("res:",res);
// },err=>{
// console.log("err:",err);
// })
//* 2.傳入一個對象鸠删,這個對象有實現(xiàn)then方法抱完,由該then方法決定后續(xù)狀態(tài)
new Promise((resolve,reject)=>{
const obj={
then:function(resolve,reject){
// resolve("resolve message");
reject("reject message")
}
}
resolve(obj);
}).then(res=>{
console.log("res:",res);
},err=>{
console.log("err:",err);
})
5.then方法
5.1 then方法-接受兩個參數(shù)
then方法是Promise對象的一個方法,它其實是 放在Promise的原型上的Promise.prototype.then
then方法接受兩個參數(shù):
- fulfilled的回調(diào)函數(shù):當(dāng)狀態(tài)變成fulfilled時會回調(diào)的函數(shù)
- rejected的回調(diào)函數(shù):當(dāng)狀態(tài)變成rejected時會回調(diào)的函數(shù)
promise.then(res=>{
console.log("res:",res);
},err=>{
console.log("err:",err);
})
// 等價于下面
promise.then(res=>{
console.log("res:",res);
}).catch(err=>{
console.log("err:",err);
})
5.2 then方法-可以多次調(diào)用
同一個promise對象的then方法可以調(diào)用多次刃泡。
當(dāng)resolve方法被回調(diào)時巧娱,所有傳入到then方法的回調(diào)函數(shù)會執(zhí)行。
// * Promise有哪些對象方法
// console.log(Promise.prototype);
// console.log(Object.getOwnPropertyDescriptors(Promise.prototype));
const promise=new Promise((resolve,reject)=>{
resolve("hahahaha")
})
// *同一個promise可以多次調(diào)用then方法
// * 當(dāng)我們的resolve方法被回調(diào)時烘贴,所有的then方法傳入的回調(diào)函數(shù)都會被調(diào)用
promise.then((res)=>{
console.log("res:",res);
})
promise.then((res)=>{
console.log("res2:",res);
})
promise.then((res)=>{
console.log("res3:",res);
})
5.3 then方法——返回值
其實then方法有返回值禁添,返回的是新的Promise對象
then方法的回調(diào)函數(shù)也有返回值,當(dāng)then方法中的回調(diào)函數(shù)返回一個結(jié)果時桨踪,那么它處于fulfilled狀態(tài)老翘,并且會將結(jié)果作為resolve的參數(shù)
-
如果返回的是一個普通的值(數(shù)字、字符串锻离、對象)铺峭,這個值會作為then方法返回的Promise對象的resolve的值
-
如果沒有返回值,則默認(rèn)返回的是undefined
const promise=new Promise((resolve,reject)=>{ resolve("hahahaha") }) promise.then(res=>{ // *如果回調(diào)函數(shù)沒有返回值汽纠,則返回的是一個undefined return {name:"wjy"} }).then(res=>{ console.log("res:",res); })
-
-
如果返回的是一個Promise對象卫键,
- 這個返回的Promise對象會決定then方法返回的Promise的狀態(tài)
const promise=new Promise((resolve,reject)=>{ resolve("hahahaha") }) // *如果返回的是一個Promise對象 promise.then(res=>{ return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve("hello wjy") },3000) }) }).then(res=>{ console.log("res:",res); })
- 如果返回的是一個對象,這個對象實現(xiàn)了thenable(then方法)虱朵,會立即執(zhí)行then方法莉炉,并根據(jù)then方法的結(jié)果來決定Promise的狀態(tài)钓账。
// *如果返回了是一個對象,并且這個對象上
promise.then(res=>{
return { //* new Promise(resolve=>resolve(obj.then))
then:function(resolve,reject){
resolve(2222)
}
}
}).then(res=>{
console.log("res:",res);
})
6.catch方法
then方法內(nèi)部可以傳入兩個參數(shù)(兩個回調(diào)函數(shù))
當(dāng)resolve函數(shù)被執(zhí)行時絮宁,會調(diào)用then方法的第一個參數(shù)(回調(diào)函數(shù))
-
當(dāng)reject函數(shù)被執(zhí)行時梆暮,會調(diào)用then方法的第二個參數(shù)(回調(diào)函數(shù))【或者內(nèi)部發(fā)生錯誤,也會被then方法的第二個參數(shù)捕獲到绍昂√杼悖】
const promise=new Promise((resolve,reject)=>{ reject("rejected status") }) promise.then(undefined,err=>{ console.log("err:",err); })
const promise=new Promise((resolve,reject)=>{ throw new Error("rejected status") }) promise.then(undefined,err=>{ console.log("err:",err); })
如果將成功和失敗的回調(diào)函數(shù)寫在一塊,閱讀性差治专,比如下面:
const promise=new Promise((resolve,reject)=>{
// reject("rejected status")
throw new Error("rejected status")
})
promise.then(res=>{
console.log("res:",res);
},err=>{
console.log("err:",err);
})
支持另外一種方法:
通過catch方法傳入錯誤或拒絕的捕獲的回調(diào)函數(shù):
const promise=new Promise((resolve,reject)=>{
reject("rejected status")
})
// * 通過catch方法傳入錯誤或(拒絕)捕獲的回調(diào)函數(shù)
// promise/a+規(guī)范
promise.catch(err=>{
console.log("err:",err)
})
還支持另外一種寫法:
- catch會優(yōu)先捕獲第一個Promise對象的異常捕獲卖陵,如果第一個Promise對象沒有發(fā)生異常,則不會使用张峰,會留著下一次使用泪蔫。如果第二個Promise對象發(fā)生異常,這個catch會去捕獲喘批。
- 這個catch方法比較特殊
const promise=new Promise((resolve,reject)=>{
reject("rejected status")
// throw new Error("rejected status")
})
promise.then(res=>{
return 111
}).catch(err=>{
console.log("err:",err)
})
const promise=new Promise((resolve,reject)=>{
resolve("resolve status")
})
promise.then(res=>{
throw new Error("發(fā)生錯誤")
return 111
}).catch(err=>{
console.log("err:",err);//*如果第一個promise對象沒有異常撩荣,所以就不會執(zhí)行,等著下一次使用,會給下一個Promise對象使用
console.log("------");
})
6.1 拒絕捕獲的問題
// * 拒絕捕獲的問題(前面課程)
const promise=new Promise((resolve,reject)=>{
reject("rejected status");//* 有點類似拋出異常
})
promise.then(res=>{
console.log("res:",res);
})
promise.catch(err=>{
console.log("err:",err);
})
這個代碼運行后會報錯的饶深。
- 之間不會相互影響的
- 因為在then方法中沒有進(jìn)行捕獲餐曹,以異常的方式退出。
- 如果代碼中沒有then方法就不會報錯
改成下面這種就不會報錯:
// * 拒絕捕獲的問題(前面課程)
const promise=new Promise((resolve,reject)=>{
reject("rejected status");//* 有點類似拋出異常
})
promise.then(res=>{
console.log("res:",res);
}).catch(err=>{
console.log("err:",err);
})
或
const promise=new Promise((resolve,reject)=>{
reject("rejected status");//* 有點類似拋出異常
})
promise.then(res=>{
console.log("res:",res);
},err=>{
console.log("err:",err);
})
6.2 reject和throw同時存在拋出哪個異常
如果執(zhí)行了reject()敌厘,throw會被忽略
如果執(zhí)行了throw台猴,throw后面的代碼都不會再執(zhí)行。
6.3 catch方法多次調(diào)用
catch方法也是Promise對象上的一個方法俱两,它也是放在Promise的原型上的Promise.prototype.catch方法饱狂。
同一個Promise對象的catch方法可以被調(diào)用多次
- 每當(dāng)調(diào)用我們可以傳入對應(yīng)的reject回調(diào)
- 當(dāng)Promise的狀態(tài)變成rejected的時候,這些回調(diào)函數(shù)都會被執(zhí)行宪彩。
const promise=new Promise((resolve,reject)=>{
reject("aaa");
})
promise.catch(err=>{
console.log("err1:",err);
})
promise.catch(err=>{
console.log("err2:",err);
})
promise.catch(err=>{
console.log("err3:",err);
})
6.4 catch方法——返回值
事實上catch方法也是會返回一個Promise對象的休讳,所以catch方法后面我們可以繼續(xù)調(diào)用then方法或者catch方法
const promise=new Promise((resolve,reject)=>{
reject("aaa");
})
promise.then(res=>{
console.log("res:",res);
}).catch(err=>{
console.log("err:",err);
return "catch return value"
}).then(res=>{
console.log("res:",res);
})
const promise=new Promise((resolve,reject)=>{
reject("aaa");
})
promise.then(res=>{
console.log("res:",res);
}).catch(err=>{
console.log("err:",err);
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve("my name")
},3000)
})
}).then(res=>{
console.log("res:",res);
})
7.fanally方法
fanally是ES9(ES2018)新增的一個特性:表示無論Promise的狀態(tài)變?yōu)閒ulfilled還是reject狀態(tài),最終都會被執(zhí)行的代碼尿孔。
finally方法是不接收參數(shù)的俊柔,因為無論前面是fulfilled狀態(tài),還是reject狀態(tài)活合,它都會執(zhí)行雏婶。
const promise=new Promise((resolve,reject)=>{
resolve("resolve message");
})
promise.then(res=>{
console.log("res:",res);
}).catch(err=>{
console.log("err:",err);
}).finally(()=>{
console.log("finally code execute");
})
finally其實也是返回一個Promise對象,但是其實很少人會用它
8.類方法——resolve方法
前面我們學(xué)習(xí)的then芜辕、catch尚骄、finally方法都屬于Promise的實例方法,都是存放在Promise的prototype上的侵续。
下面我們將學(xué)習(xí)類方法
有時候我們已經(jīng)有一個現(xiàn)成的內(nèi)容倔丈,希望將其轉(zhuǎn)成Promise來使用憨闰,這個時候我們可以使用Promise.resolve方法來完成。
- Promise.resolve()方法:相當(dāng)于new Promise需五,并且執(zhí)行resolve操作鹉动。
resolve的參數(shù)形式:
- 情況一:參數(shù)是一個普通的值或者對象
- 情況二:參數(shù)是一個Promise對象
- 情況三:參數(shù)是一個thenable
function foo(){
const obj={name:"wjy"}
return new Promise((resolve)=>{
resolve(obj);
});
}
foo().then(res=>{
console.log("res:",res);
})
等價于下面:
function foo(){
const obj={name:"wjy"}
return Promise.resolve(obj);
}
foo().then(res=>{
console.log("res:",res);
})
8.1 參數(shù)形式
// * 1.傳入普通的值
// const promise=Promise.resolve({name:"wjy"});
// * 2.傳入一個Promise,由這個新的Promise對象決定原Promise對象的狀態(tài)
// const promise=Promise.resolve(new Promise((resolve,reject)=>{
// // resolve("resolve2 message")
// reject("reject message")
// }))
// promise.then(res=>{
// console.log("res:",res);
// }).catch(err=>{
// console.log("err:",err);
// })
// * 3. 傳入thenable對象(實現(xiàn)了then方法),會立即執(zhí)行then方法宏邮,由then方法的結(jié)果決定其Promise的狀態(tài)
const promise=Promise.resolve({
then:function(resolve,reject){
// resolve("resolve ");
reject("reject message")
}
})
promise.then(res=>{
console.log("res:",res);
}).catch(err=>{
console.log("err:",err);
})
9.類方法——reject方法
reject方法類似于resolve方法垫挨,只是會將Promise對象的狀態(tài)設(shè)置為rejected狀態(tài)
Promise.reject的用法相當(dāng)于new Promise乾颁,并且會調(diào)用reject
Promise.reject無論傳過來的參數(shù)是什么狀態(tài)澜汤,都會直接作為reject的參數(shù)傳遞到catch的稠诲。
10.類方法——all方法
它的作用是將多個Promise包裹在一起,形成一個新的Promise
新的Promise狀態(tài)由包裹的所有Promise決定
- 當(dāng)所有的Promise狀態(tài)變?yōu)閒ulfilled狀態(tài)時飒炎,新的Promise狀態(tài)為fulfilled埋哟,并且會將所有的Promise的返回值組成一個數(shù)組
- 當(dāng)有一個Promise狀態(tài)變?yōu)閞ejected狀態(tài)時,新的Promise狀態(tài)為rejected郎汪,并且會將第一個reject的返回值作為參數(shù)
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1111)
},1000)
})
const p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(22222)
},2000)
})
const p3=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(33333)
},3000)
})
// * 需求:所有的Promise都變成fulfilled,再拿到結(jié)果
//* 意外:在拿到所有結(jié)果之前赤赊,有一個promise變成了rejected,那么整個promise是rejected
Promise.all([p1,p2,p3,"aaaa"]).then(res=>{
console.log("res:",res);
}).catch(err=>{
// * 最后轉(zhuǎn)到catch執(zhí)行
console.log("err:",err);
})
// * all:所有的promise為fulfilled時才會執(zhí)行then方法,如果有一個狀態(tài)為rejected煞赢,就會轉(zhuǎn)成執(zhí)行catch方法
11.類方法——allSettled方法
all方法有一個缺陷:當(dāng)有一個Promise變成rejected狀態(tài)時抛计,新Promise就會立即變成對應(yīng)的rejected狀態(tài)。
- 那么對于resolved的照筑,以及仍然處于pending狀態(tài)的Promise吹截,我們是獲取不到對應(yīng)的結(jié)果的。
在ES11(ES2020)中朦肘,添加了新的API Promise.allSettled
- 該方法會在所有的Promise都有結(jié)果(settled)饭弓,無論是fulfilled,還是rejected時媒抠,才會有最終的狀態(tài)。
- 并且這個Promise的結(jié)果一定是fulfilled的
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1111)
},1000)
})
const p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(22222)
},2000)
})
const p3=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(33333)
},3000)
})
// allSettled
// * 新的Promise的狀態(tài)一定是fulfilled
Promise.allSettled([p1,p2,p3]).then(res=>{
console.log("res:",res);
}).catch(err=>{
console.log("err:",err);
})
12. 類方法——race
如果有一個Promise有了結(jié)果咏花,我們就希望決定最終新的Promise的狀態(tài)趴生,那么可以使用race方法
- race是競技、競賽的意思昏翰,表示多個Promise相互競爭苍匆,誰先有結(jié)果,那么就使用誰的結(jié)果棚菊。
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1111)
},1000)
})
const p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(22222)
},2000)
})
const p3=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(33333)
},3000)
})
// * race :競技 競賽
Promise.race([p1,p2,p3]).then(res=>{
console.log("res:",res);
}).catch(err=>{
console.log("err:",err);
})
13.類方法——any方法
any方法是ES12中新增的方法浸踩,和race方法是類似的:
- any方法會等到一個fulfilled狀態(tài),才會決定新Promise的狀態(tài)
- 如果所有的Promise都是rejected的统求,那么也會等到所有的Promise都變成rejected狀態(tài)检碗。
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(1111)
},1000)
})
const p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(22222)
},2000)
})
const p3=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(33333)
},3000)
})
// * any
Promise.any([p1,p2,p3]).then(res=>{
console.log("res:",res);
}).catch(err=>{
// *可以通過.errors拿到合計的錯誤信息
console.log("err:",err.errors);
})
- 如果所有的Promise都是reject的据块,那么會報一個AggregateError的錯誤。
二折剃、手寫Promise
1.Promise的設(shè)計和構(gòu)造方法
const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_REJECTED="rejected";
// * 自己手寫的Promise要符合的一定的規(guī)范另假,要符合PromiseA+
class HYPromise{
// * 記錄狀態(tài)
constructor(executor) {
// * 保存Promise的狀態(tài)
this.status=PROMISE_STATUS_PENDING;
// * 保存?zhèn)魅氲闹? this.value=undefined;
this.reason=undefined;
const resolve=(value)=>{
if(this.status==PROMISE_STATUS_PENDING){
this.status=PROMISE_STATUS_FULFILLED;
this.value=value;
console.log("resolve被調(diào)用");
}
}
const reject=(reason)=>{
if(this.status==PROMISE_STATUS_PENDING){
this.status=PROMISE_STATUS_REJECTED;
this.reason=reason;
console.log("reject被調(diào)用");
}
}
executor(resolve,reject)
}
}
const promise=new HYPromise((resolve,reject)=>{
console.log("狀態(tài)pending");
resolve("1111")
})
2.then方法的設(shè)計
const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_REJECTED="rejected";
// * 自己手寫的Promise要符合的一定的規(guī)范,要符合PromiseA+
class HYPromise{
// * 記錄狀態(tài)
constructor(executor) {
// * 保存Promise的狀態(tài)
this.status=PROMISE_STATUS_PENDING;
// * 保存?zhèn)魅氲闹? this.value=undefined;
this.reason=undefined;
const resolve=(value)=>{
if(this.status==PROMISE_STATUS_PENDING){
this.status=PROMISE_STATUS_FULFILLED;
queueMicrotask(()=>{ //* 定時器是一個宏任務(wù)怕犁,會放在下一次事件循環(huán)時使用
this.value=value;
console.log("resolve被調(diào)用");
// * 執(zhí)行then傳入進(jìn)來的第一個回調(diào)函數(shù)
this.onFulfilled(this.value)
})
}
}
const reject=(reason)=>{
if(this.status==PROMISE_STATUS_PENDING){
this.status=PROMISE_STATUS_REJECTED;
queueMicrotask(()=>{
this.reason=reason;
console.log("reject被調(diào)用");
// * 執(zhí)行then傳入進(jìn)來的第二個回調(diào)函數(shù)
this.onRejected(this.reason);
})
}
}
executor(resolve,reject)
}
then(onFulfilled,onRejected){
this.onFulfilled=onFulfilled;
this.onRejected=onRejected;
}
}
const promise=new HYPromise((resolve,reject)=>{
console.log("狀態(tài)pending");
// resolve("1111")
reject("reject message")
})
promise.then(res=>{
console.log("res:",res);
},err=>{
console.log("err:",err);
})
promise.then(res=>{
console.log("res2:",res);
},err=>{
console.log("err2:",err);
})
// * 多次調(diào)用then边篮,后面的then方法會覆蓋前面的then方法,而且只會調(diào)用一次方法
// * then方法不能實現(xiàn)鏈?zhǔn)秸{(diào)用
3.then方法的優(yōu)化一
- 實現(xiàn)then多次調(diào)用
- 如果Promise狀態(tài)已經(jīng)確定奏甫,再次調(diào)用then方法戈轿,傳入的回調(diào)函數(shù)應(yīng)該直接執(zhí)行
const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_REJECTED="rejected";
// * 自己手寫的Promise要符合的一定的規(guī)范,要符合PromiseA+
class HYPromise{
// * 記錄狀態(tài)
constructor(executor) {
// * 保存Promise的狀態(tài)
this.status=PROMISE_STATUS_PENDING;
// * 保存?zhèn)魅氲闹? this.value=undefined;
this.reason=undefined;
this.onFulfilledFns=[];
this.onRejectedFns=[];
const resolve=(value)=>{
if(this.status==PROMISE_STATUS_PENDING){
queueMicrotask(()=>{ //* 定時器是一個宏任務(wù)阵子,會放在下一次事件循環(huán)時使用
this.status=PROMISE_STATUS_FULFILLED;
this.value=value;
console.log("resolve被調(diào)用");
// * 執(zhí)行then傳入進(jìn)來的第一個回調(diào)函數(shù)
this.onFulfilledFns.forEach(Fn=>{
Fn(this.value)
})
})
}
}
const reject=(reason)=>{
if(this.status==PROMISE_STATUS_PENDING){
queueMicrotask(()=>{
this.status=PROMISE_STATUS_REJECTED;
this.reason=reason;
console.log("reject被調(diào)用");
// * 執(zhí)行then傳入進(jìn)來的第二個回調(diào)函數(shù)
this.onRejectedFns.forEach(Fn=>{
Fn(this.reason)
})
})
}
}
executor(resolve,reject)
}
then(onFulfilled,onRejected){
// 1.如果then方法調(diào)用的時候凶杖,狀態(tài)已經(jīng)確定下來了,應(yīng)該直接執(zhí)行的
if(this.status===PROMISE_STATUS_FULFILLED&&onFulfilled){
onFulfilled(this.value);
}else if(this.status===PROMISE_STATUS_REJECTED&&onRejected){
onRejected(this.reason)
}else{
// 將成功回調(diào)和失敗回調(diào)添加到數(shù)組中
this.onFulfilledFns.push(onFulfilled);
this.onRejectedFns.push(onRejected);
}
}
}
const promise=new HYPromise((resolve,reject)=>{
console.log("狀態(tài)pending");
resolve("1111");//resolved/fulfilled
})
promise.then(res=>{
console.log("res:",res);
},err=>{
console.log("err:",err);
})
promise.then(res=>{
console.log("res2:",res);
},err=>{
console.log("err2:",err);
})
setTimeout(()=>{
promise.then(res=>{
console.log(res);
})
},1000)
// * 解決了then方法的多次調(diào)用
// * 在確定Promise狀態(tài)之后款筑,再次調(diào)用then
// const promise2=new Promise((resolve,reject)=>{
// resolve("aaaaa");
// })
// setTimeout(()=>{
// promise2.then(res=>{ //原生編寫了
// console.log(res);
// })
// },1000)
4.then方法的優(yōu)化二
- 實現(xiàn)了一個鏈?zhǔn)秸{(diào)用(返回一個新的Promise對象智蝠,使用新的Promise對象的resolve方法去處理對應(yīng)的回調(diào)函數(shù)的返回值)
- 實現(xiàn)了對executor執(zhí)行時發(fā)生異常(錯誤),應(yīng)該執(zhí)行reject函數(shù)
- 后面封裝了一個工具函數(shù)奈梳,用來處理異常和回調(diào)
const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_REJECTED="rejected";
// 工具函數(shù)
function execFunctionWithCatchError(exeFn,value,resolve,reject){
try{
let result=exeFn(value);
reject(result);
}catch(err){
reject(err);
}
}
// * 自己手寫的Promise要符合的一定的規(guī)范杈湾,要符合PromiseA+
class HYPromise{
// * 記錄狀態(tài)
constructor(executor) {
// * 保存Promise的狀態(tài)
this.status=PROMISE_STATUS_PENDING;
// * 保存?zhèn)魅氲闹? this.value=undefined;
this.reason=undefined;
this.onFulfilledFns=[];
this.onRejectedFns=[];
const resolve=(value)=>{
if(this.status==PROMISE_STATUS_PENDING){
// * 添加微任務(wù)
queueMicrotask(()=>{ //* 定時器是一個宏任務(wù),會放在下一次事件循環(huán)時使用
if(this.status!==PROMISE_STATUS_PENDING) return;
this.status=PROMISE_STATUS_FULFILLED;
this.value=value;
// console.log("resolve被調(diào)用");
// * 執(zhí)行then傳入進(jìn)來的第一個回調(diào)函數(shù)
this.onFulfilledFns.forEach(Fn=>{
Fn(this.value);
})
})
}
}
const reject=(reason)=>{
if(this.status==PROMISE_STATUS_PENDING){
// * 添加微任務(wù)
queueMicrotask(()=>{
if(this.status!==PROMISE_STATUS_PENDING) return;
this.status=PROMISE_STATUS_REJECTED;
this.reason=reason;
// console.log("reject被調(diào)用");
// * 執(zhí)行then傳入進(jìn)來的第二個回調(diào)函數(shù)
this.onRejectedFns.forEach(Fn=>{
Fn(this.reason)
})
})
}
}
// * 在調(diào)用executor時判斷里面是否拋出異常
try{
executor(resolve,reject)
}catch(err){
reject(err)
}
}
then(onFulfilled,onRejected){
return new HYPromise((resolve,reject)=>{
// 1.如果then方法調(diào)用的時候攘须,狀態(tài)已經(jīng)確定下來了漆撞,應(yīng)該直接執(zhí)行的
if(this.status===PROMISE_STATUS_FULFILLED&&onFulfilled){
execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
}else if(this.status===PROMISE_STATUS_REJECTED&&onRejected){
execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
}else{
// 將成功回調(diào)和失敗回調(diào)添加到數(shù)組中
this.onFulfilledFns.push(()=>{
execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
});
this.onRejectedFns.push(()=>{
execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
});
}
})
}
}
const promise=new HYPromise((resolve,reject)=>{
// console.log("狀態(tài)pending");
// resolve("1111");//resolved/fulfilled
reject("2222")
// throw new Error("創(chuàng)建就報的異常")
})
promise.then(res=>{
console.log("res:",res);
return "aaaa"
},err=>{
console.log("err:",err);
return "err bbb"
}).then(res=>{
console.log("res2:",res);
},err=>{
console.log("err2:",err);
})
// * 1. 實現(xiàn)了鏈?zhǔn)秸{(diào)用,鏈?zhǔn)秸{(diào)用的前提是返回一個新的HYPromise于宙,最重要的是 什么時候執(zhí)行resolve浮驳,什么時候執(zhí)行reject
// * 一般在正常情況下,無論原HYPromise的狀態(tài)是如何捞魁,后面都會執(zhí)行新的HYPromise的resolve至会,并將其值作為參數(shù)
// * 只有在代碼發(fā)生異常時(錯誤),才會讓新的HYPromise執(zhí)行reject
// * 2. 在HYPromise的executor執(zhí)行過程中如果發(fā)生異常谱俭,應(yīng)該要執(zhí)行reject
5.catch方法的設(shè)計
- catch只會接受一個onRejected的回調(diào)函數(shù)
- 在內(nèi)部調(diào)用then方法奉件,設(shè)置then方法的第一個參數(shù)為undefined,第二個參數(shù)的值為onRejected
// * catch方法的設(shè)計
catch(onRejected){
this.then(undefined,onRejected);
}
但是這樣還是不能捕獲原Promise對象的rejected狀態(tài),因為這個catch方法是針對新的Promise的rejected的狀態(tài)昆著,如果想要捕獲原Promise的rejected狀態(tài)县貌,需要拋出異常,新的Promise對象的catch方法才能捕獲到凑懂。
所以我們需要改寫then方法:
- 在方法內(nèi)部前面判斷第二個參數(shù)是否有值煤痕,如果沒有值,就重新賦值為一個函數(shù),函數(shù)內(nèi)部拋出一個異常
這樣在新的Promise就能捕獲到原來的promise的rejected的狀態(tài)
then(onFulfilled,onRejected){
onRejected=onRejected||(err=>{throw err})
//以下代碼省略...
}
全部的代碼如下:
const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_REJECTED="rejected";
// 工具函數(shù)
function execFunctionWithCatchError(exeFn,value,resolve,reject){
try{
let result=exeFn(value);
reject(result);
}catch(err){
reject(err);
}
}
// * 自己手寫的Promise要符合的一定的規(guī)范摆碉,要符合PromiseA+
class HYPromise{
// * 記錄狀態(tài)
constructor(executor) {
// * 保存Promise的狀態(tài)
this.status=PROMISE_STATUS_PENDING;
// * 保存?zhèn)魅氲闹? this.value=undefined;
this.reason=undefined;
this.onFulfilledFns=[];
this.onRejectedFns=[];
const resolve=(value)=>{
if(this.status==PROMISE_STATUS_PENDING){
// * 添加微任務(wù)
queueMicrotask(()=>{ //* 定時器是一個宏任務(wù)塘匣,會放在下一次事件循環(huán)時使用
if(this.status!==PROMISE_STATUS_PENDING) return;
this.status=PROMISE_STATUS_FULFILLED;
this.value=value;
// console.log("resolve被調(diào)用");
// * 執(zhí)行then傳入進(jìn)來的第一個回調(diào)函數(shù)
this.onFulfilledFns.forEach(Fn=>{
Fn(this.value);
})
})
}
}
const reject=(reason)=>{
if(this.status==PROMISE_STATUS_PENDING){
// * 添加微任務(wù)
queueMicrotask(()=>{
if(this.status!==PROMISE_STATUS_PENDING) return;
this.status=PROMISE_STATUS_REJECTED;
this.reason=reason;
// console.log("reject被調(diào)用");
// * 執(zhí)行then傳入進(jìn)來的第二個回調(diào)函數(shù)
this.onRejectedFns.forEach(Fn=>{
Fn(this.reason)
})
})
}
}
// * 在調(diào)用executor時判斷里面是否拋出異常
try{
executor(resolve,reject)
}catch(err){
reject(err)
}
}
then(onFulfilled,onRejected){
onRejected=onRejected||(err=>{throw err})
return new HYPromise((resolve,reject)=>{
// 1.如果then方法調(diào)用的時候,狀態(tài)已經(jīng)確定下來了兆解,應(yīng)該直接執(zhí)行的
if(this.status===PROMISE_STATUS_FULFILLED&&onFulfilled){
execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
}else if(this.status===PROMISE_STATUS_REJECTED&&onRejected){
execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
}else{
// 將成功回調(diào)和失敗回調(diào)添加到數(shù)組中
if(onFulfilled)this.onFulfilledFns.push(()=>{
execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
});
if(onRejected)this.onRejectedFns.push(()=>{
execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
});
}
})
}
// * catch方法的設(shè)計
catch(onRejected){
this.then(undefined,onRejected);
}
}
const promise=new HYPromise((resolve,reject)=>{
console.log("狀態(tài)pending");
// resolve("1111");//resolved/fulfilled
reject("2222")
// throw new Error("創(chuàng)建就報的異常")
})
promise.then(res=>{
console.log("res:",res);
},err=>{
console.log("err1:",err);
}).catch(err=>{
console.log("err:",err);
})
6.finally方法的設(shè)計
const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_REJECTED="rejected";
// 工具函數(shù)
function execFunctionWithCatchError(exeFn,value,resolve,reject){
try{
const result=exeFn(value);
resolve(result);
}catch(err){
reject(err);
}
}
class HYPromise{
constructor(executor){
this.status=PROMISE_STATUS_PENDING;
this.value=undefined;
this.reason=undefined;
// this.onFulfilled=undefined;
// this.onRejected=undefined;
this.onFulfilledFns=[];
this.onRejectedFns=[];
const resolve=(value)=>{
if(this.status==PROMISE_STATUS_PENDING){
// * 原生執(zhí)行then方法時是一個微任務(wù)
queueMicrotask(()=>{
if(this.status!==PROMISE_STATUS_PENDING) return;
this.status=PROMISE_STATUS_FULFILLED;
this.value=value;
// * 執(zhí)行then傳入進(jìn)來的回調(diào)函數(shù)
// this.onFulfilled(this.value);
this.onFulfilledFns.forEach(Fn=>{
Fn(this.value);
})
})
}
}
const reject=(reason)=>{
if(this.status==PROMISE_STATUS_PENDING){
queueMicrotask(()=>{
if(this.status!==PROMISE_STATUS_PENDING) return;
this.status=PROMISE_STATUS_REJECTED;
this.reason=reason;
// * 執(zhí)行then傳入進(jìn)來的第二個參數(shù)
// this.onRejected(this.reason);
this.onRejectedFns.forEach(Fn=>{
Fn(this.reason);
})
})
}
}
try{
executor(resolve,reject)
}catch(err){
reject(err);
}
}
then(onFulfilled,onRejected){
onRejected=onRejected|| (err=>{ throw err});
onFulfilled=onFulfilled|| (value=>value)
return new HYPromise((resolve,reject)=>{
if(this.status==PROMISE_STATUS_FULFILLED&&onFulfilled){
execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
}else if(this.status==PROMISE_STATUS_REJECTED&&onRejected){
execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
}else{
if(onFulfilled)this.onFulfilledFns.push(()=>{
execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
});
if(onRejected)this.onRejectedFns.push(()=>{
execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
});
}
})
}
catch(onRejected){
return this.then(undefined,onRejected);
}
finally(onFinally){
return this.then(()=>{
onFinally();
},()=>{
onFinally();
})
}
}
const promise=new HYPromise((resolve,reject)=>{
console.log("狀態(tài)pending");
// reject("111");
resolve("222");
// throw new Error("pending err")
})
promise.then(res=>{
console.log("res:",res);
}).catch(err=>{
console.log("catch err:",err);
}).finally(()=>{
console.log("finally方法實現(xiàn)");
})
// * 實現(xiàn)finally方法
7.類方法 resolve和reject的設(shè)計
const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_REJECTED="rejected";
// 工具函數(shù)
function execFunctionWithCatchError(exeFn,value,resolve,reject){
try{
const result=exeFn(value);
resolve(result);
}catch(err){
reject(err);
}
}
class HYPromise{
constructor(executor){
this.status=PROMISE_STATUS_PENDING;
this.value=undefined;
this.reason=undefined;
// this.onFulfilled=undefined;
// this.onRejected=undefined;
this.onFulfilledFns=[];
this.onRejectedFns=[];
const resolve=(value)=>{
if(this.status==PROMISE_STATUS_PENDING){
// * 原生執(zhí)行then方法時是一個微任務(wù)
queueMicrotask(()=>{
if(this.status!==PROMISE_STATUS_PENDING) return;
this.status=PROMISE_STATUS_FULFILLED;
this.value=value;
// * 執(zhí)行then傳入進(jìn)來的回調(diào)函數(shù)
// this.onFulfilled(this.value);
this.onFulfilledFns.forEach(Fn=>{
Fn(this.value);
})
})
}
}
const reject=(reason)=>{
if(this.status==PROMISE_STATUS_PENDING){
queueMicrotask(()=>{
if(this.status!==PROMISE_STATUS_PENDING) return;
this.status=PROMISE_STATUS_REJECTED;
this.reason=reason;
// * 執(zhí)行then傳入進(jìn)來的第二個參數(shù)
// this.onRejected(this.reason);
this.onRejectedFns.forEach(Fn=>{
Fn(this.reason);
})
})
}
}
try{
executor(resolve,reject)
}catch(err){
reject(err);
}
}
then(onFulfilled,onRejected){
onRejected=onRejected|| (err=>{ throw err});
onFulfilled=onFulfilled|| (value=>value)
return new HYPromise((resolve,reject)=>{
if(this.status==PROMISE_STATUS_FULFILLED&&onFulfilled){
execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
}else if(this.status==PROMISE_STATUS_REJECTED&&onRejected){
execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
}else{
if(onFulfilled)this.onFulfilledFns.push(()=>{
execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
});
if(onRejected)this.onRejectedFns.push(()=>{
execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
});
}
})
}
catch(onRejected){
return this.then(undefined,onRejected);
}
finally(onFinally){
return this.then(()=>{
onFinally();
},()=>{
onFinally();
})
}
static resolve(value){
return new HYPromise((resolve,reject)=>{
resolve(value);
})
}
static reject(reason){
return new HYPromise((resolve,reject)=>{
reject(reason);
})
}
}
HYPromise.resolve(123).then(res=>{
console.log("res:",res);
})
HYPromise.reject("err message").catch(err=>{
console.log("err:",err);
})
// * 實現(xiàn)了類方法 resolve和reject方法
8. 類方法 all 和allSettled
const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_REJECTED="rejected";
// 工具函數(shù)
function execFunctionWithCatchError(exeFn,value,resolve,reject){
try{
const result=exeFn(value);
resolve(result);
}catch(err){
reject(err);
}
}
class HYPromise{
constructor(executor){
this.status=PROMISE_STATUS_PENDING;
this.value=undefined;
this.reason=undefined;
// this.onFulfilled=undefined;
// this.onRejected=undefined;
this.onFulfilledFns=[];
this.onRejectedFns=[];
const resolve=(value)=>{
if(this.status==PROMISE_STATUS_PENDING){
// * 原生執(zhí)行then方法時是一個微任務(wù)
queueMicrotask(()=>{
if(this.status!==PROMISE_STATUS_PENDING) return;
this.status=PROMISE_STATUS_FULFILLED;
this.value=value;
// * 執(zhí)行then傳入進(jìn)來的回調(diào)函數(shù)
// this.onFulfilled(this.value);
this.onFulfilledFns.forEach(Fn=>{
Fn(this.value);
})
})
}
}
const reject=(reason)=>{
if(this.status==PROMISE_STATUS_PENDING){
queueMicrotask(()=>{
if(this.status!==PROMISE_STATUS_PENDING) return;
this.status=PROMISE_STATUS_REJECTED;
this.reason=reason;
// * 執(zhí)行then傳入進(jìn)來的第二個參數(shù)
// this.onRejected(this.reason);
this.onRejectedFns.forEach(Fn=>{
Fn(this.reason);
})
})
}
}
try{
executor(resolve,reject)
}catch(err){
reject(err);
}
}
then(onFulfilled,onRejected){
onRejected=onRejected|| (err=>{ throw err});
onFulfilled=onFulfilled|| (value=>value)
return new HYPromise((resolve,reject)=>{
if(this.status==PROMISE_STATUS_FULFILLED&&onFulfilled){
execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
}else if(this.status==PROMISE_STATUS_REJECTED&&onRejected){
execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
}else{
if(onFulfilled)this.onFulfilledFns.push(()=>{
execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
});
if(onRejected)this.onRejectedFns.push(()=>{
execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
});
}
})
}
catch(onRejected){
return this.then(undefined,onRejected);
}
finally(onFinally){
return this.then(()=>{
onFinally();
},()=>{
onFinally();
})
}
static resolve(value){
return new HYPromise((resolve,reject)=>{
resolve(value);
})
}
static reject(reason){
return new HYPromise((resolve,reject)=>{
reject(reason);
})
}
static all(promises){
// * 問題關(guān)鍵:什么時候執(zhí)行resolve馆铁,什么時候執(zhí)行reject
return new HYPromise((resolve,reject)=>{
let values=[];
promises.forEach(promise=>{
promise.then(res=>{
values.push(res);
if(values.length==promises.length) resolve(values);
}).catch(err=>{
reject(err);
})
})
})
}
static allSettled(promises){
return new Promise((resolve)=>{
let results=[];
promises.forEach(promise=>{
promise.then(res=>{
results.push({status:PROMISE_STATUS_FULFILLED,value:res})
if(results.length==promises.length)
resolve(results);
}).catch(err=>{
results.push({status:PROMISE_STATUS_REJECTED,value:err})
if(results.length==promises.length)
resolve(results);
})
})
})
}
}
const p1=new Promise((resolve)=>{
setTimeout(()=>{
resolve(1111)
},1000)
})
const p2=new Promise((resolve)=>{
setTimeout(()=>{
resolve(2222)
},2000)
})
const p3=new Promise((resolve)=>{
setTimeout(()=>{
resolve(3333)
},3000)
})
// *all方法會產(chǎn)生一個新的Promise對象,這個新的Promise對象是由前面的所有的Promise來決定其狀態(tài)
// * 如果全部為fulfilled锅睛,則會執(zhí)行then方法埠巨,如果有一個為rejected,則直接執(zhí)行catch方法
HYPromise.all([p1,p2,p3]).then(res=>{
console.log("res:",res);
}).catch(err=>{
console.log("err:",err);
})
// * allSettled:它會等所有的Promise都有結(jié)果现拒,然后再執(zhí)行resolve方法辣垒,它是沒有reject方法的執(zhí)行
// * 實現(xiàn)了all方法:
HYPromise.allSettled([p1,p2,p3]).then(res=>{
console.log("res:",res);
}).catch(err=>{
console.log("err:",err);
})
9. 類方法 race和any
const PROMISE_STATUS_FULFILLED="fulfilled";
const PROMISE_STATUS_PENDING="pending";
const PROMISE_STATUS_REJECTED="rejected";
// 工具函數(shù)
function execFunctionWithCatchError(exeFn,value,resolve,reject){
try{
const result=exeFn(value);
resolve(result);
}catch(err){
reject(err);
}
}
class HYPromise{
constructor(executor){
this.status=PROMISE_STATUS_PENDING;
this.value=undefined;
this.reason=undefined;
// this.onFulfilled=undefined;
// this.onRejected=undefined;
this.onFulfilledFns=[];
this.onRejectedFns=[];
const resolve=(value)=>{
if(this.status==PROMISE_STATUS_PENDING){
// * 原生執(zhí)行then方法時是一個微任務(wù)
queueMicrotask(()=>{
if(this.status!==PROMISE_STATUS_PENDING) return;
this.status=PROMISE_STATUS_FULFILLED;
this.value=value;
// * 執(zhí)行then傳入進(jìn)來的回調(diào)函數(shù)
// this.onFulfilled(this.value);
this.onFulfilledFns.forEach(Fn=>{
Fn(this.value);
})
})
}
}
const reject=(reason)=>{
if(this.status==PROMISE_STATUS_PENDING){
queueMicrotask(()=>{
if(this.status!==PROMISE_STATUS_PENDING) return;
this.status=PROMISE_STATUS_REJECTED;
this.reason=reason;
// * 執(zhí)行then傳入進(jìn)來的第二個參數(shù)
// this.onRejected(this.reason);
this.onRejectedFns.forEach(Fn=>{
Fn(this.reason);
})
})
}
}
try{
executor(resolve,reject)
}catch(err){
reject(err);
}
}
then(onFulfilled,onRejected){
onRejected=onRejected|| (err=>{ throw err});
onFulfilled=onFulfilled|| (value=>value)
return new HYPromise((resolve,reject)=>{
if(this.status==PROMISE_STATUS_FULFILLED&&onFulfilled){
execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
}else if(this.status==PROMISE_STATUS_REJECTED&&onRejected){
execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
}else{
if(onFulfilled)this.onFulfilledFns.push(()=>{
execFunctionWithCatchError(onFulfilled,this.value,resolve,reject);
});
if(onRejected)this.onRejectedFns.push(()=>{
execFunctionWithCatchError(onRejected,this.reason,resolve,reject);
});
}
})
}
catch(onRejected){
return this.then(undefined,onRejected);
}
finally(onFinally){
return this.then(()=>{
onFinally();
},()=>{
onFinally();
})
}
static resolve(value){
return new HYPromise((resolve,reject)=>{
resolve(value);
})
}
static reject(reason){
return new HYPromise((resolve,reject)=>{
reject(reason);
})
}
static all(promises){
// * 問題關(guān)鍵:什么時候執(zhí)行resolve,什么時候執(zhí)行reject
return new HYPromise((resolve,reject)=>{
let values=[];
promises.forEach(promise=>{
promise.then(res=>{
values.push(res);
if(values.length==promises.length) resolve(values);
}).catch(err=>{
reject(err);
})
})
})
}
static allSettled(promises){
return new HYPromise((resolve)=>{
let results=[];
promises.forEach(promise=>{
promise.then(res=>{
results.push({status:PROMISE_STATUS_FULFILLED,value:res})
if(results.length==promises.length)
resolve(results);
}).catch(err=>{
results.push({status:PROMISE_STATUS_REJECTED,value:err})
if(results.length==promises.length)
resolve(results);
})
})
})
}
static race(promises){
return new HYPromise((resolve,reject)=>{
promises.forEach(promise=>{
// promise.then(res=>{
// resolve(res);
// }).catch(err=>{
// reject(err);
// })
promise.then(resolve,reject)
})
})
}
static any(promises){
// * resolve必須等待有一個成功的結(jié)果
// * reject 所有的都失敗才執(zhí)行 reject
return new HYPromise((resolve,reject)=>{
let count=0;
let reasons=[];
promises.forEach(promise=>{
promise.then(res=>{
resolve(res)
}).catch(err=>{
reasons.push(err);
if(reasons.length==promises.length){
// reject(reasons)
reject(new AggregateError(reasons," AggregateError: All promises were rejected"))
}
})
})
})
}
}
// const p1=new Promise((resolve)=>{
// setTimeout(()=>{
// resolve(1111)
// },1000)
// })
// const p2=new Promise((resolve)=>{
// setTimeout(()=>{
// resolve(2222)
// },2000)
// })
// const p3=new Promise((resolve)=>{
// setTimeout(()=>{
// resolve(3333)
// },3000)
// })
// HYPromise.race([p1,p2,p3]).then(res=>{
// console.log("res:",res);
// }).catch(err=>{
// console.log("err:",err);
// })
// * any方法
const p1=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(1111)
},1000)
})
const p2=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(2222)
},2000)
})
const p3=new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(3333)
},3000)
})
HYPromise.any([p1,p2,p3]).then(res=>{
console.log("res:",res);
}).catch(err=>{
console.log("err:",err.errors);
})