1、Promise的使用
1.1 前期
在Promise出現(xiàn)之前,我們?nèi)绾卧诎l(fā)送網(wǎng)絡(luò)請(qǐng)求后對(duì)響應(yīng)的結(jié)果進(jìn)行處理呢?
function requestUrl(url, success, failure) {
setTimeout(() => {
if (url == "true url") {
success("result");
} else {
failure("error");
}
}, 50);
}
此時(shí),我們通常會(huì)借助回調(diào)函數(shù),如代碼中的 success和failure就對(duì)應(yīng)著響應(yīng)成功和失敗時(shí)調(diào)用的回調(diào)函數(shù),使用如上方案可以解決請(qǐng)求函數(shù)得到結(jié)果之后,獲取到對(duì)應(yīng)的回調(diào),但是它存在兩個(gè)主要的問題:
- 我們需要自己來設(shè)計(jì)回調(diào)函數(shù)、回調(diào)函數(shù)的名稱、回調(diào)函數(shù)的使用等;
- 對(duì)于不同的人、不同的框架設(shè)計(jì)出來的方案是不同的,那么我們必須耐心去看別人的源碼或者文檔,以便可以理解它這個(gè)函數(shù)到底怎么用;
1.2 Promise的出現(xiàn)
Promise是一個(gè)類,可以翻譯成 承諾、許諾 、期約;
- 當(dāng)我們需要給予調(diào)用者一個(gè)承諾:待會(huì)兒我會(huì)給你回調(diào)數(shù)據(jù)時(shí),就可以創(chuàng)建一個(gè)Promise的對(duì)象;
- 在通過new創(chuàng)建Promise對(duì)象時(shí),我們需要傳入一個(gè)回調(diào)函數(shù),我們稱之為executor
這個(gè)回調(diào)函數(shù)會(huì)被立即執(zhí)行,并且接受另外兩個(gè)回調(diào)函數(shù)resolve、reject作為參數(shù);
當(dāng)我們調(diào)用resolve回調(diào)函數(shù)時(shí),會(huì)執(zhí)行Promise對(duì)象的then方法傳入的回調(diào)函數(shù);
當(dāng)我們調(diào)用reject回調(diào)函數(shù)時(shí),會(huì)執(zhí)行Promise對(duì)象的catch方法傳入的回調(diào)函數(shù);
Promise的結(jié)構(gòu)
const promise = new Promise((resolve, reject) => {
resolve("success");
reject("error");
});
promise
.then(() => {
console.log(res); //result
})
.catch((err) => {
console.log(err); //error
});
我們可以將Promise劃分成三個(gè)狀態(tài):
- 待定(pending): 初始狀態(tài),既沒有被兌現(xiàn),也沒有被拒絕;
當(dāng)執(zhí)行executor中的代碼時(shí),處于該狀態(tài); - 已兌現(xiàn)(fulfilled): 意味著操作成功完成;
執(zhí)行了resolve時(shí),處于該狀態(tài); - 已拒絕(rejected): 意味著操作失敗;
執(zhí)行了reject時(shí),處于該狀態(tài);
1.3 Promise重構(gòu)
function requestUrl(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (url == "true url") {
resolve("success");
} else {
reject("error");
}
}, 50);
});
}
2、Promise詳解
2.1 Executor
Executor是在創(chuàng)建Promise時(shí)需要傳入的一個(gè)回調(diào)函數(shù),這個(gè)回調(diào)函數(shù)會(huì)被立即執(zhí)行,并且接受兩個(gè)函數(shù)作為參數(shù):
new Promise((resolve, reject) => {
})
通常我們會(huì)在Executor中確定我們的Promise狀態(tài):
- 通過resolve,可以兌現(xiàn)(fulfilled)Promise的狀態(tài),我們也可以稱之為已決議(resolved);
- 通過reject,可以拒絕(reject)Promise的狀態(tài);
一旦狀態(tài)被確定下來,Promise的狀態(tài)會(huì)被 鎖死,該P(yáng)romise的狀態(tài)是不可更改的
在我們調(diào)用resolve的時(shí)候,如果resolve傳入的值本身不是一個(gè)Promise,那么會(huì)將該P(yáng)romise的狀態(tài)變成 兌現(xiàn)(fulfilled);
在之后我們?nèi)フ{(diào)用reject時(shí),已經(jīng)不會(huì)有任何的響應(yīng)了(并不是這行代碼不會(huì)執(zhí)行,而是無法改變Promise狀態(tài));
2.2 resolve不同值的區(qū)別
- 情況一:如果
resolve傳入一個(gè)普通的值或者對(duì)象,那么這個(gè)值會(huì)作為then回調(diào)的參數(shù); - 情況二:如果
resolve中傳入的是另外一個(gè)Promise,那么這個(gè)新Promise會(huì)決定原Promise的狀態(tài): - 情況三:如果
resolve中傳入的是一個(gè)對(duì)象,并且這個(gè)對(duì)象有實(shí)現(xiàn)then方法,那么會(huì)執(zhí)行該then方法,并且根據(jù)
then方法的結(jié)果來決定Promise的狀態(tài):

2.3 then方法
-
then方法是Promise對(duì)象上的一個(gè)方法:它其實(shí)是放在Promise的原型上的Promise.prototype.then
then方法接受兩個(gè)參數(shù):
fulfilled的回調(diào)函數(shù):當(dāng)狀態(tài)變成fulfilled時(shí)會(huì)回調(diào)的函數(shù);
reject的回調(diào)函數(shù):當(dāng)狀態(tài)變成reject時(shí)會(huì)回調(diào)的函數(shù);

-
一個(gè)Promise的then方法是可以被多次調(diào)用的:
每次調(diào)用我們都可以傳入對(duì)應(yīng)的fulfilled回調(diào);
當(dāng)Promise的狀態(tài)變成fulfilled的時(shí)候,這些回調(diào)函數(shù)都會(huì)被執(zhí)行;
image.png then方法本身是有返回值的,它的返回值是一個(gè)Promise,所以我們可以進(jìn)行如下的鏈?zhǔn)秸{(diào)用,then方法返回的Promise到底處于什么樣的狀態(tài)呢?
Promise有三種狀態(tài),那么這個(gè)Promise處于什么狀態(tài)呢?
a、當(dāng)then方法中的回調(diào)函數(shù)本身在執(zhí)行的時(shí)候,那么它處于pending狀態(tài);
b、當(dāng)then方法中的回調(diào)函數(shù)返回一個(gè)結(jié)果時(shí),那么它處于fulfilled狀態(tài),并且會(huì)將結(jié)果作為resolve的參數(shù);
情況一:返回一個(gè)普通的值;
情況二:返回一個(gè)Promise;
情況三:返回一個(gè)thenable值;
c、當(dāng)then方法拋出一個(gè)異常時(shí),那么它處于reject狀態(tài);
2.4 catch方法
catch方法也是Promise對(duì)象上的一個(gè)方法:它也是放在Promise的原型上的 Promise.prototype.catch
-
一個(gè)Promise的catch方法是可以被多次調(diào)用的:
每次調(diào)用我們都可以傳入對(duì)應(yīng)的reject回調(diào);
當(dāng)Promise的狀態(tài)變成reject的時(shí)候,這些回調(diào)函數(shù)都會(huì)被執(zhí)行
image.png 返回值:catch方法也是會(huì)返回一個(gè)Promise對(duì)象的,所以catch方法后面我們可以繼續(xù)調(diào)用then方法或者catch方法
2.5 finally方法
finally是在ES9(ES2018)中新增的一個(gè)特性:表示無論P(yáng)romise對(duì)象無論變成fulfilled還是reject狀態(tài),最終都會(huì)被執(zhí)行的代碼。
finally方法是不接收參數(shù)的,因?yàn)闊o論前面是fulfilled狀態(tài),還是reject狀態(tài),它都會(huì)執(zhí)行。

2.6 resolve方法
有時(shí)候我們已經(jīng)有一個(gè)現(xiàn)成的內(nèi)容了,希望將其轉(zhuǎn)成Promise來使用,這個(gè)時(shí)候我們可以使用 Promise.resolve 方法來完成。
-
Promise.resolve的用法相當(dāng)于new Promise,并且執(zhí)行resolve操作
image.png resolve參數(shù)的形態(tài):
情況一:參數(shù)是一個(gè)普通的值或者對(duì)象
情況二:參數(shù)本身是Promise
情況三:參數(shù)是一個(gè)thenable
2.7 reject方法
reject方法類似于resolve方法,只是會(huì)將Promise對(duì)象的狀態(tài)設(shè)置為reject狀態(tài)。
Promise.reject的用法相當(dāng)于new Promise,只是會(huì)調(diào)用reject:
Promise.reject傳入的參數(shù)無論是什么形態(tài),都會(huì)直接作為reject狀態(tài)的參數(shù)傳遞到catch的。
2.8 all方法
- 將多個(gè)Promise包裹在一起形成一個(gè)新的Promise;新的Promise狀態(tài)由包裹的所有Promise共同決定:
- 當(dāng)所有的Promise狀態(tài)變成fulfilled狀態(tài)時(shí),新的Promise狀態(tài)為fulfilled,并且會(huì)將所有Promise的返回值
組成一個(gè)數(shù)組; - 當(dāng)有一個(gè)Promise狀態(tài)為reject時(shí),新的Promise狀態(tài)為reject,并且會(huì)將第一個(gè)reject的返回值作為參數(shù);

2.9 allSettled方法
- all方法有一個(gè)缺陷:當(dāng)有其中一個(gè)Promise變成reject狀態(tài)時(shí),新Promise就會(huì)立即變成對(duì)應(yīng)的reject狀態(tài)。
那么對(duì)于resolved的,以及依然處于pending狀態(tài)的Promise,我們是獲取不到對(duì)應(yīng)的結(jié)果的; -
在ES11(ES2020)中,添加了新的API Promise.allSettled:
該方法會(huì)在所有的Promise都有結(jié)果(settled),無論是fulfilled,還是reject時(shí),才會(huì)有最終的狀態(tài);
并且這個(gè)Promise的結(jié)果一定是fulfilled的;
image.png
打印的結(jié)果為
image.png
2.10 race方法
如果有一個(gè)Promise有了結(jié)果,我們就希望決定最終新Promise的狀態(tài),那么可以使用race方法:
race是競(jìng)技、競(jìng)賽的意思,表示多個(gè)Promise相互競(jìng)爭(zhēng),誰(shuí)先有結(jié)果,那么就使用誰(shuí)的結(jié)果;

2.11 any方法(至少有一個(gè)resolve)
any方法是ES12中新增的方法,和race方法是類似的:
pany方法會(huì)等到一個(gè)fulfilled狀態(tài),才會(huì)決定新Promise的狀態(tài);
如果所有的Promise都是reject的,那么也會(huì)等到所有的Promise都變成rejected狀態(tài);

如果所有的Promise都是reject的,那么會(huì)報(bào)一個(gè)AggregateError的錯(cuò)誤。




