
接上一篇,繼續(xù)介紹了 Promise 相關(guān) API。
一、Promise.resolve()
Promise.resolve() 方法的作用就是將某個(gè)值(非 Promise 對(duì)象實(shí)例)轉(zhuǎn)換為 Promise 對(duì)象實(shí)例。
const promise = Promise.resolve('foo')
// 相當(dāng)于
const promise = new Promise(resolve => resolve('foo'))
需要注意的是,它仍然會(huì)遵循 Event Loop 機(jī)制,包括后面介紹的其他 API。具體執(zhí)行順序本文不展開(kāi)討論。
Promise.resolve() 方法的參數(shù)分為四種情況:
1. 不帶任何參數(shù)
它返回一個(gè)狀態(tài)為 fulfilled,值為 undefined 的 Promise 實(shí)例對(duì)象。
const promise = Promise.resolve()
// promise 結(jié)果:
// {
// [[Prototype]]: Promise,
// [[PromiseState]]: "fulfilled",
// [[PromiseResult]]: undefined
// }
2. 參數(shù)是一個(gè) Promise 實(shí)例對(duì)象
這時(shí),Promise.resolve() 將會(huì)不做任何修改、原封不動(dòng)地返回該實(shí)例。
請(qǐng)注意,即使參數(shù)是一個(gè) rejected 狀態(tài)的 Promise 實(shí)例,返回的實(shí)例也不會(huì)變成 fulfilled 狀態(tài),不要被這個(gè) resolve 字面意思誤解了。
const p1 = new Promise(resolve => resolve({ name: 'Frankie' })) // "fulfilled"
const p2 = new Promise((resolve, reject) => reject({ name: 'Frankie' })) // "rejected"
const p3 = Promise.resolve(p1)
const p4 = Promise.resolve(p2)
console.log(p1 === p3) // true
console.log(p2 === p4) // true
// p3 結(jié)果:
// {
// [[Prototype]]: Promise,
// [[PromiseState]]: "fulfilled",
// [[PromiseResult]]: { name: 'Frankie' }
// }
// p4 結(jié)果:
// {
// [[Prototype]]: Promise,
// [[PromiseState]]: "rejected",
// [[PromiseResult]]: { name: 'Frankie' }
// }
其實(shí)這種情況,就是上一篇提到過(guò)的。
const p5 = new Promise(resolve => resovle(1))
const p6 = new Promise(resolve => {
reslove(p5)
// 注意,不要嘗試在此處調(diào)用 Promise.resolve(),會(huì)導(dǎo)致無(wú)限遞歸。
})
上面示例中,p6 的狀態(tài)取決于 p5 的狀態(tài)。
3. 參數(shù)是一個(gè) thenable 對(duì)象
thenable 對(duì)象,是指具有 then 方法的對(duì)象。例如:
const obj = {
then: function(resolve, reject) {
resolve('foo')
}
}
上面示例中,obj 對(duì)象就是一個(gè) thenable 對(duì)象。Promise.resolve() 方法會(huì)將這個(gè) thenable 對(duì)象轉(zhuǎn)為 Promise 對(duì)象,然后就立即執(zhí)行 thenable 對(duì)象的 then() 方法。
const obj = {
then: function (resolve, reject) {
console.log(2)
resolve('foo')
// reject('foo') // 如果是這樣,最終 promise 對(duì)象將會(huì)變成了 rejected 狀態(tài)。
}
}
const promise = Promise.resolve(obj)
promise.then(res => {
console.log(3)
console.log(res) // "foo"
})
console.log(1)
// 打印結(jié)果分別是 1、2、3、"foo"
// promise 結(jié)果:
// {
// [[Prototype]]: Promise,
// [[PromiseState]]: "fulfilled",
// [[PromiseResult]]: "foo"
// }
上述示例中,obj 對(duì)象的 then() 方法執(zhí)行后,對(duì)象 promise 的狀態(tài)變成了 fulfilled,接著執(zhí)行最后的那個(gè) promise.then() 方法,打印出 "foo"。
4. 參數(shù)是一個(gè)不具有 then() 方法的對(duì)象,或者壓根不是一個(gè)對(duì)象,而是原始值。
如果是這種情況,Promise.resolve() 方法返回一個(gè)新的 Promise 對(duì)象,狀態(tài)為 fulfilled,且該實(shí)例對(duì)象的值,就是該參數(shù)值。
const p1 = Promise.resolve('foo')
const p2 = Promise.resolve({})
// p1 結(jié)果:
// {
// [[Prototype]]: Promise,
// [[PromiseState]]: "fulfilled",
// [[PromiseResult]]: "foo"
// }
// p2 結(jié)果:
// {
// [[Prototype]]: Promise,
// [[PromiseState]]: "fulfilled",
// [[PromiseResult]]: {}
// }
在實(shí)際項(xiàng)目中,一般是第 4 種情況居多,我似乎真的沒(méi)見(jiàn)過(guò)前三種情況的。
二、Promise.reject()
Promise.reject() 方法會(huì)返回一個(gè)新的 Promise 實(shí)例對(duì)象,該實(shí)例的狀態(tài)總是為 rejected。
const promise = Promise.reject('foo')
// 相當(dāng)于
const promise = new Promise((resolve, reject) => reject('foo'))
跟 Promise.resolve() 不同的是,Promise.reject() 方法的參數(shù)(無(wú)論是原始值、普通對(duì)象、還是 Promise 實(shí)例對(duì)象),將會(huì)原封不動(dòng)地作為返回實(shí)例對(duì)象的值。
Promise.reject('Oops').catch(err => {
console.log(err === 'Oops') // true
// do something...
})
三、Promise.all()
Promise.all() 方法用于將多個(gè) Promise 實(shí)例,包裝成一個(gè)新的 Promise 實(shí)例。
const promise = Promise.all([p1, p2, p3])
上面代碼中,Promise.all() 方法接受一個(gè)數(shù)組作為參數(shù),其中 p1、p2、p3 都是 Promise 實(shí)例。如果數(shù)組中包含非 Promise 實(shí)例,它們會(huì)使用 Promise.resolve() 的方法,將參數(shù)轉(zhuǎn)為 Promise 實(shí)例,再進(jìn)一步處理。另外,Promise.all() 方法的參數(shù)可以不是數(shù)組,但必須具有 Iterator 接口,且返回的每個(gè)成員都是 Promise 實(shí)例。
其中 promise 的狀態(tài)由 p1、p2、p3 決定,分為兩種情況。
只有當(dāng)
p1、p2、p3的狀態(tài)都變成fulfilled,promise的狀態(tài)才會(huì)變成fulfilled,此時(shí)p1、p2、p3實(shí)例的值,會(huì)組成一個(gè)數(shù)組,并傳遞給promise。只要
p1、p2、p3之中有一個(gè)被rejected,promise的狀態(tài)就會(huì)變成rejected。此時(shí)第一個(gè)rejected實(shí)例的值(注意,不會(huì)像上面一樣組成數(shù)組哦),會(huì)傳遞給promise。
看個(gè)例子:
const userIds = [1, 3, 5]
const promiseArr = userIds.map(id => {
return window.fetch(`/config/user/${id}`) // 假設(shè)是請(qǐng)求用戶配置
})
Promise
.all(promiseArr)
.then(res => {
// res 是一個(gè)數(shù)組,每一項(xiàng)對(duì)應(yīng)每個(gè)實(shí)例的值,即 [[PromiseResult]]
// 常見(jiàn)做法是將 res 進(jìn)行解構(gòu),即 Promise.all(promiseArr).then(([a, b, c]) => { /* do something... */ })
// 假設(shè) promiseArr 是一個(gè)空的可迭代對(duì)象,例如空數(shù)組,Promise.all([]) 實(shí)例狀態(tài)為 fulfilled,值為 []。
// do something...
})
.catch(err => {
// err 為 Promise.all() 被 rejected 的原因(reason)
})
上面的示例中,promiseArr 是包含 3 個(gè) Promise 實(shí)例的數(shù)組,只有這 3 個(gè)實(shí)例的狀態(tài)都變成 fulfilled,或其中一個(gè)變?yōu)?rejected,才會(huì)調(diào)用 Promise.all() 方法的回調(diào)函數(shù)。
四、Promise.race()
Promise.race() 方法同樣是將多個(gè) Promise 實(shí)例,包裝成一個(gè)新的 Promise 實(shí)例。
const promise = Promise.race([p1, p2, p3])
Promise.race() 方法同樣接受一個(gè)可迭代對(duì)象,只要 p1、p2、p3 中有一個(gè)實(shí)例率先改變狀態(tài)(fulfiled 或 rejected),promise 的狀態(tài)就會(huì)跟著改變,而且 promise 實(shí)例的值就是率先改變的實(shí)例的返回值。若可迭代對(duì)象中的某一項(xiàng)不是 Promise 實(shí)例,仍會(huì)使用 Promise.resolve() 進(jìn)行轉(zhuǎn)換。
當(dāng)傳遞一個(gè)空的可迭代對(duì)象,那么 Promise.race() 實(shí)例的狀態(tài)將會(huì)一直停留在 pending。這點(diǎn)跟 Promise.all() 是不同的。
const p1 = Promise.all([])
const p2 = Promise.race([])
setTimeout(() => {
console.log(p1) // Promise {<fulfilled>: Array(0)}
console.log(p2) // Promise {<pending>}
})
五、Promise.allSettled()
Promise.allSettled() 是 ES11 標(biāo)準(zhǔn)引入的一個(gè)方法,同樣還是將多個(gè) Promise 實(shí)例包裝成一個(gè)新的 Promise 實(shí)例。只有等所有實(shí)例都返回結(jié)果(無(wú)論是 fulfilled 還 rejected),包裝實(shí)例的狀態(tài)才會(huì)發(fā)生變化。
我認(rèn)為,這算是對(duì) Promise.all() 存在 rejected 實(shí)例情況的一種補(bǔ)全吧。
注意,
Promise.allSettled()的狀態(tài),只可能是pending或fulfilled狀態(tài),不可能存在rejected狀態(tài)。即只會(huì)從pending -> fulfilled的變化。
我們來(lái)看看以下示例,各種情況的結(jié)果吧:
const p1 = Promise.reject(1)
const p2 = Promise.resolve(2)
const p3 = new Promise((resolve, reject) => {
setTimeout(() => { reject(3) }, 1000)
})
const p4 = new Promise(() => { }) // p4 狀態(tài)將會(huì)一直停留在 pending
const p5 = Promise.allSettled([]) // 參數(shù)為空迭代對(duì)象
const p6 = Promise.allSettled([p4])
const p7 = Promise.allSettled([p1, p2, p3])
setTimeout(() => {
console.log('p1:', p1)
console.log('p2:', p2)
console.log('p3:', p3)
console.log('p4:', p4)
console.log('p5:', p5)
console.log('p6:', p6)
console.log('p7:', p7)
p5.then(res => {
console.log('p5 then:', res)
})
p6.then(res => {
// 這里將不會(huì)執(zhí)行,因?yàn)?p6 一直處于 pending 狀態(tài)
console.log('p6 then:', res)
})
p7.then(res => {
console.log('p7 then:', res)
})
}, 2000)

列舉以上示例,是為了得出以下結(jié)論:
Promise.allSettled()一定要等到參數(shù)中每一個(gè)Promise狀態(tài)定型后,它返回的實(shí)例對(duì)象才會(huì)定型為fulfilled狀態(tài)。否則只會(huì)是pending狀態(tài)。類(lèi)似
Promise.allSettled([])把一個(gè)空數(shù)組(空的迭代對(duì)象)作為參數(shù),最后實(shí)例的狀態(tài)為fulfilled,且實(shí)例的值為空數(shù)組[]。-
注意
Promise.allSettled()返回的實(shí)例的值,首先它是一個(gè)數(shù)組,而數(shù)組每項(xiàng)都是一個(gè)對(duì)象,該對(duì)象的屬性取決于對(duì)應(yīng)參數(shù)Promise實(shí)例的狀態(tài)。例如
p1的狀態(tài)為rejected,p2的狀態(tài)為fulfilled。因此包裝實(shí)例的前兩項(xiàng)的對(duì)象分別為{ status: "rejected", reason: 1 }、{ status: "fulfilled", value: 2 },其他項(xiàng)同理。其中status屬性只會(huì)是fulfilled或rejected兩個(gè)字符串值。主要區(qū)別在于value屬性和reason屬性,即fulfilled狀態(tài)對(duì)應(yīng)value屬性,而rejected狀態(tài)對(duì)應(yīng)reason屬性。
有時(shí)候,我們不關(guān)心異步操作的結(jié)果,只關(guān)心這些操作有沒(méi)有結(jié)束。這時(shí),使用 Promise.allSettled() 方法就很有用了。而 Promise.all() 是沒(méi)辦法確保這一點(diǎn)的。
六、Promise.any()
在 ES12 標(biāo)準(zhǔn)中,引入了 Promise.any() 方法,它用于將多個(gè) Promise 實(shí)例,包裝成一個(gè)新的 Promise 實(shí)例。
Promise.any() 接受一個(gè) Promise 可迭代對(duì)象,只要參數(shù)實(shí)例中有一個(gè)變成 fulfilled 狀態(tài),包裝實(shí)例就會(huì)變成 fulfilled 狀態(tài),其值就是參數(shù)實(shí)例的值。
Promise.any() 與 Promise.race() 很像,只有一個(gè)不同點(diǎn),就是 Promise.any() 不會(huì)因?yàn)槟硞€(gè)參數(shù) Promise 實(shí)例變成 rejected 狀態(tài)而接受,必須要等到所有參數(shù)實(shí)例的狀態(tài)都變?yōu)?rejected,包裝實(shí)例的狀態(tài)才會(huì)是 rejected。
const p1 = Promise.reject(1)
const p2 = Promise.resolve(2)
const p3 = new Promise((resolve, reject) => {
setTimeout(() => { reject(3) }, 1000)
})
const p4 = new Promise(() => { }) // p4 狀態(tài)將會(huì)一直停留在 pending
const p5 = Promise.any([]) // p5 會(huì)變成 rejected 狀態(tài)
const p6 = Promise.any([p4])
const p7 = Promise.any([p1, p2, p3])
const p8 = Promise.any([p1, p3])
setTimeout(() => {
console.log('p1:', p1)
console.log('p2:', p2)
console.log('p3:', p3)
console.log('p4:', p4)
console.log('p5:', p5)
console.log('p6:', p6)
console.log('p7:', p7)
p5.then(res => {
console.log('p5 then:', res)
}).catch(err => {
// p5 的狀態(tài)會(huì)變成 rejected,因此會(huì)執(zhí)行到這里。
console.log('p5 catch:', err)
})
p6.then(res => {
// p6 的狀態(tài)一直會(huì)是 pending,因此不會(huì)執(zhí)行回調(diào)。
console.log('p6 then:', res)
})
p7.then(res => {
console.log('p7 then:', res)
})
p8.then(res => {
console.log('p8 then:', res)
}).catch(err => {
// 注意 err 是一個(gè)對(duì)象
console.log('p8 catch:', err)
console.dir(err)
})
}, 2000)

當(dāng) Promise.any() 返回的實(shí)例變成 rejected 時(shí),其實(shí)例的值是 AggregateError 實(shí)例。但傳遞一個(gè)空的迭代對(duì)象,Promise.any() 包裝實(shí)例也會(huì)變成 rejected 狀態(tài),如 p5。
七、總結(jié)
關(guān)于 Promise.all()、Promise.race()、Promise.allSettled()、Promise.any() 方法,總結(jié)以下特點(diǎn)。
它們的用處都是將多個(gè)
Promise實(shí)例,包裝成一個(gè)新的Promise實(shí)例。它們都接受一個(gè)具有 Iterator 接口的可迭代對(duì)象,通常為數(shù)組。且會(huì)返回一個(gè)新的
Promise實(shí)例對(duì)象。它們處理參數(shù)為空的可迭代對(duì)象的方式不一樣,本來(lái)就是要處理多個(gè)
Promise對(duì)象,才會(huì)用到它們,所以這種情況無(wú)需理會(huì)。真遇到再回來(lái)翻閱文檔即可,現(xiàn)在我寫(xiě)到這里都記不太清楚其中的區(qū)別了,但問(wèn)題不大。
Promise.all()當(dāng)所有實(shí)例均為fulfilled狀態(tài),最終的包裝實(shí)例才會(huì)是fulfilled,其值是一個(gè)數(shù)組。否則將會(huì)是rejected狀態(tài);
Promise.race()則是某個(gè)實(shí)例的狀態(tài)發(fā)生變化,最終包裝實(shí)例將對(duì)應(yīng)率先變化實(shí)例所對(duì)應(yīng)的值和狀態(tài)?!鞍l(fā)生變化”是指pending -> fulfilled或pending -> rejected。
Promise.allSettled()單從命名上來(lái)猜測(cè),就知道它需要等所有參數(shù)實(shí)例確定狀態(tài)后,包裝實(shí)例的狀態(tài)才會(huì)變成fulfilled狀態(tài),注意它不存在rejected狀態(tài)的情況。包裝實(shí)例的返回值是一個(gè)數(shù)組,數(shù)組每項(xiàng)可能是{ status: "fulfilled", value: /* 對(duì)應(yīng) fulfilled 的值 */ }或{ status: "rejected", reason: /* 對(duì)應(yīng) rejected 的原因 */ },取決于每個(gè)參數(shù)實(shí)例的狀態(tài)。
Promise.any()當(dāng)某個(gè)參數(shù)實(shí)例的狀態(tài)變?yōu)?fulfilled,那么包裝實(shí)例就定型了,對(duì)應(yīng)該參數(shù)實(shí)例的狀態(tài)和值。否則它必須等到所有參數(shù)實(shí)例變?yōu)?rejected狀態(tài),包裝實(shí)例的狀態(tài)才會(huì)發(fā)生改變,變?yōu)?rejected,其值是一個(gè)AggregateError實(shí)例。
The end.