Promise
Promise 就像這個(gè)詞的表面意識(shí)一樣纹冤,表示一種承諾、許諾购公,會(huì)在后面給出一個(gè)結(jié)果萌京,成功 或者 失敗。現(xiàn)在已經(jīng)成為了主流的異步編程的操作方式宏浩,寫(xiě)進(jìn)了標(biāo)準(zhǔn)里面知残。
狀態(tài)
Promise 有且僅有三種狀態(tài):
- 待定(pending): 初始狀態(tài),既沒(méi)有被兌現(xiàn)比庄,也沒(méi)有被拒絕求妹。
- 已兌現(xiàn)(fulfilled): 意味著操作成功完成。
- 已拒絕(rejected): 意味著操作失敗印蔗。
其實(shí)我覺(jué)得這個(gè)模型還是挺簡(jiǎn)單扒最,也很好理解丑勤。就好像华嘹,你跟你女朋友求婚,她跟你說(shuō)她要考慮一下法竞,明天才能給你答案耙厚,這就是承諾(promise)。同時(shí)岔霸,這也是一個(gè)等待的過(guò)程(pending)薛躬,然后你就等,等到明天你女朋友給你答復(fù)呆细,同意(fulfilled)或者拒絕(rejected)型宝,如果同意就準(zhǔn)備結(jié)婚了,如果不同意就等下次再求婚,哈哈哈趴酣。
那狀態(tài)要怎么改變呢梨树?
Promise 的構(gòu)造函數(shù)需要傳入一個(gè)函數(shù) executor ,這個(gè)函數(shù)需要兩個(gè)入?yún)⒎謩e是 resolve 和 rejected 兩個(gè)函數(shù)岖寞,這兩個(gè)方法主要是用來(lái)修改狀態(tài)的抡四。當(dāng)我們調(diào)用 resolve 函數(shù)的時(shí)候,Promise 的狀態(tài)就變成 fulfilled 仗谆。當(dāng)我們調(diào)用 reject 函數(shù)的時(shí)候指巡,Promise 的狀態(tài)就變成 reject 。舉個(gè)栗子:
new Promise((resolve, reject) => {
console.log("開(kāi)始求婚隶垮。")
console.log("藻雪。。岁疼。阔涉。。")
console.log("考慮一下捷绒。")
setTimeout(() => {
if (isHandsome || isRich) {
resolve('我同意瑰排!')
} else {
reject("拒絕:我們八字不合")
}
}, 2000)
})
這里還要注意一點(diǎn),如果一個(gè) promise 已經(jīng)被兌現(xiàn)(fulfilled)或被拒絕(rejected)暖侨,那么我們也可以說(shuō)它處于已敲定(settled)狀態(tài)椭住。
鏈?zhǔn)秸{(diào)用
這點(diǎn)跟 Rx 還是挺相似的,也是鏈?zhǔn)秸{(diào)用字逗,這樣的好處就是異步邏輯更加清晰京郑,連貫,符合直覺(jué)葫掉,避免了回調(diào)地獄些举。
我們可以用 Promise.then()
,Promise.catch()
和 Promise.finally()
這些方法將進(jìn)一步的操作與一個(gè)變?yōu)橐亚枚顟B(tài)的 Promise 關(guān)聯(lián)起來(lái)俭厚。這些方法還會(huì)返回一個(gè)新生成的 Promise 對(duì)象户魏,這個(gè)對(duì)象可以被非強(qiáng)制性的用來(lái)做鏈?zhǔn)秸{(diào)用,就像這樣:
const myPromise =
(new Promise(myExecutorFunc))
.then(handleFulfilledA,handleRejectedA)
.then(handleFulfilledB,handleRejectedB)
.then(handleFulfilledC,handleRejectedC);
// 或者挪挤,這樣可能會(huì)更好...
const myPromise =
(new Promise(myExecutorFunc))
.then(handleFulfilledA)
.then(handleFulfilledB)
.then(handleFulfilledC)
.catch(handleRejectedAny);
我們一般推薦使用第二種的寫(xiě)法叼丑,因?yàn)榈谝环N寫(xiě)法如果在中間出錯(cuò),在 .then
中處理完之后扛门,會(huì)繼續(xù)走下一個(gè) .then
的 onfulfilled
函數(shù)鸠信,不太符合直覺(jué)。當(dāng)然论寨,如果你有馬上處理異常的需求也可以這樣寫(xiě)星立。
當(dāng) .then()
中缺少能夠返回 Promise 對(duì)象的函數(shù)時(shí)爽茴,鏈?zhǔn)秸{(diào)用就直接繼續(xù)進(jìn)行下一環(huán)操作。因此绰垂,鏈?zhǔn)秸{(diào)用可以在最后一個(gè) .catch()
之前把所有的 handleRejection
都省略掉闹啦。類似地, .catch()
其實(shí)只是沒(méi)有給 handleFulfilled
預(yù)留參數(shù)位置的 .then()
而已辕坝。
還是一個(gè) Promise
這里要特別注意一個(gè)點(diǎn)窍奋,Promise.then()
、Promise.catch()
酱畅、Promise.finally()
這些方法會(huì)返回一個(gè)新的 Promise琳袄,這一點(diǎn)在其他很多文章中會(huì)忽略這個(gè)點(diǎn)。 .then()
函數(shù)最后會(huì)返回一個(gè) Promise 纺酸,如果在 .then
的 onfulfilled
(也就是入?yún)⒌牡谝粋€(gè)函數(shù))中返回一個(gè)值窖逗,或者對(duì)象,這個(gè) Promise 的狀態(tài)就會(huì)確定(settled)餐蔬,接著會(huì)觸發(fā)下個(gè) .then()
中的 onfulfilled
碎紊,并將這個(gè) 值 或者 對(duì)象作為入?yún)魅搿?/p>
誒,等一下樊诺,如果在 .then()
中直接返回一個(gè) Promise 呢仗考?
這個(gè)時(shí)候就會(huì)直接替換返回的 Promise 。就跟 RxJava
中的 flatMap
操作挺相似的词爬。MDN 中有說(shuō)明:
鏈?zhǔn)秸{(diào)用中的 promise 們就像俄羅斯套娃一樣秃嗜,是嵌套起來(lái)的,但又像是一個(gè)棧顿膨,每個(gè)都必須從頂端被彈出锅锨。鏈?zhǔn)秸{(diào)用中的第一個(gè) promise 是嵌套最深的一個(gè),也將是第一個(gè)被彈出的恋沃。
(promise D, (promise C, (promise B, (promise A) ) ) )
當(dāng)存在一個(gè)
nextValue
是 promise 時(shí)必搞,就會(huì)出現(xiàn)一種動(dòng)態(tài)的替換效果。return
會(huì)導(dǎo)致一個(gè) promise 被彈出囊咏,但這個(gè)nextValue
promise 則會(huì)被推入被彈出 promise 原來(lái)的位置恕洲。對(duì)于上面所示的嵌套場(chǎng)景,假設(shè)與 "promise B" 相關(guān)的.then()
返回了一個(gè)值為 "promise X" 的nextValue
匆笤。那么嵌套的結(jié)果看起來(lái)就會(huì)是這樣:(promise D, (promise C, (promise X) ) )
觀察者模式研侣?
而且一個(gè) Promise 可能會(huì)參與不止一次的嵌套谱邪,感覺(jué)也像是觀察者的模式炮捧,并支持多個(gè)觀察者。對(duì)于下面的代碼惦银,promiseA
向"已敲定"("settled")狀態(tài)的過(guò)渡會(huì)導(dǎo)致兩個(gè)實(shí)例的 .then
都被調(diào)用咆课。
const promiseA = new Promise(myExecutorFunc);
const promiseB = promiseA.then(handleFulfilled1, handleRejected1);
const promiseC = promiseA.then(handleFulfilled2, handleRejected2);
寫(xiě)點(diǎn)代碼
講了這么多末誓,寫(xiě)點(diǎn)代碼吧,就寫(xiě)一個(gè)书蚪,女朋友多次拒絕之后看到了你的誠(chéng)意喇澡,最后同意的故事。手動(dòng)狗頭殊校。
let count = 0
const propose = () => {
return new Promise((resolve, reject) => {
console.log("開(kāi)始求婚晴玖。")
console.log("。为流。呕屎。。敬察。")
console.log("考慮一下秀睛。")
setTimeout(() => {
if (count < 3) {
reject("拒絕:我們八字不合")
count++
} else {
resolve('我同意!')
}
}, 2000)
})
}
const startPropose = () => {
propose()
.then((result) => {
console.log(result)
console.log("完結(jié)撒花")
console.log("莲祸、·蹂安、·、锐帜、·田盈、·、·缴阎、")
},)
.catch((e) => {
{
console.error(e)
console.log("被拒絕了缠黍,下次再求婚。")
setTimeout(() => {
startPropose()
}, 3000)
}
})
}
startPropose()
補(bǔ)充:
還有一些小細(xì)節(jié)药蜻。
Promise 一旦創(chuàng)建瓷式,傳入的 executor 會(huì)被立刻執(zhí)行。
一個(gè)“已敲定”("settled")狀態(tài)的 Promise 也可以接受操作语泽,也就是說(shuō)贸典,后面可以接 then 或者 catch 函數(shù)。
-
所有 Promise 都是異步的踱卵,即便是“已敲定”(“settled”)了的 Promise 也是如此廊驼。一個(gè)已經(jīng)處于"已敲定"("settled")狀態(tài)的 promise 中的操作只有 promise 鏈?zhǔn)秸{(diào)用的棧被清空了和一個(gè)事件循環(huán)過(guò)去了之后才會(huì)被執(zhí)行。這種效果跟
setTimeout(action, 10)
特別相似惋砂。const promiseA = new Promise( (resolutionFunc,rejectionFunc) => { resolutionFunc(777); }); // 這時(shí)妒挎,"promiseA" 已經(jīng)被敲定了。 promiseA.then( (val) => console.log("asynchronous logging has val:",val) ); console.log("immediate logging"); // produces output in this order: // immediate logging // asynchronous logging has val: 777
-
其他一些靜態(tài)方法
-
這個(gè)方法返回一個(gè)新的promise對(duì)象西饵,該promise對(duì)象在iterable參數(shù)對(duì)象里所有的promise對(duì)象都成功的時(shí)候才會(huì)觸發(fā)成功酝掩,一旦有任何一個(gè)iterable里面的promise對(duì)象失敗則立即觸發(fā)該promise對(duì)象的失敗。這個(gè)新的promise對(duì)象在觸發(fā)成功狀態(tài)以后眷柔,會(huì)把一個(gè)包含iterable里所有promise返回值的數(shù)組作為成功回調(diào)的返回值期虾,順序跟iterable的順序保持一致原朝;如果這個(gè)新的promise對(duì)象觸發(fā)了失敗狀態(tài),它會(huì)把iterable里第一個(gè)觸發(fā)失敗的promise對(duì)象的錯(cuò)誤信息作為它的失敗錯(cuò)誤信息镶苞。Promise.all方法常被用于處理多個(gè)promise對(duì)象的狀態(tài)集合喳坠。(可以參考jQuery.when方法---譯者注)
-
等到所有promises都已敲定(settled)(每個(gè)promise都已兌現(xiàn)(fulfilled)或已拒絕(rejected))。 返回一個(gè)promise茂蚓,該promise在所有promise完成后完成壕鹉。并帶有一個(gè)對(duì)象數(shù)組,每個(gè)對(duì)象對(duì)應(yīng)每個(gè)promise的結(jié)果聋涨。
-
接收一個(gè)Promise對(duì)象的集合御板,當(dāng)其中的一個(gè) promise 成功,就返回那個(gè)成功的promise的值牛郑。
-
當(dāng)iterable參數(shù)里的任意一個(gè)子promise被成功或失敗后怠肋,父promise馬上也會(huì)用子promise的成功返回值或失敗詳情作為參數(shù)調(diào)用父promise綁定的相應(yīng)句柄,并返回該promise對(duì)象淹朋。
-
返回一個(gè)狀態(tài)為失敗的Promise對(duì)象笙各,并將給定的失敗信息傳遞給對(duì)應(yīng)的處理方法
-
返回一個(gè)狀態(tài)由給定value決定的Promise對(duì)象。如果該值是thenable(即础芍,帶有then方法的對(duì)象)杈抢,返回的Promise對(duì)象的最終狀態(tài)由then方法執(zhí)行決定;否則的話(該value為空仑性,基本類型或者不帶then方法的對(duì)象),返回的Promise對(duì)象狀態(tài)為fulfilled惶楼,并且將該value傳遞給對(duì)應(yīng)的then方法。通常而言诊杆,如果您不知道一個(gè)值是否是Promise對(duì)象歼捐,使用Promise.resolve(value) 來(lái)返回一個(gè)Promise對(duì)象,這樣就能將該value以Promise對(duì)象形式使用。
-
參考:MDN(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise)