一文詳解如何有效的處理Promise并發(fā)
前言

如上圖所示的代碼,相信大家在平時(shí)的開發(fā)中肯定用到不少,我們可以進(jìn)行優(yōu)化,使得只用短短一半的時(shí)間就可以完成。 在上面的函數(shù)中,我們等待用戶信息請(qǐng)求完成后再請(qǐng)求詳情信息。但這兩個(gè)函數(shù)之間并沒(méi)有任何關(guān)聯(lián),所以我們可以同時(shí)觸發(fā)兩個(gè)請(qǐng)求,并同時(shí)等待完成。那么,我們有多少種方式來(lái)實(shí)現(xiàn)呢?你真的可以處理好Promise的并發(fā)么?接下來(lái),我們進(jìn)入正題。
Promise.all
如何實(shí)現(xiàn)
Promise.all 大家應(yīng)該是比較熟悉的,Promise.all方法接收一個(gè)promise的iterable類型,如果所有傳入的promise都變成完成狀態(tài),Promise.all返回的Promise異步的變?yōu)橥瓿?。如果傳入?code>promise中有一個(gè)失敗(rejected),Promise.all將失敗的結(jié)果給失敗狀態(tài)的回調(diào)函數(shù),而不管其他promise是否完成。 我們可以如下實(shí)現(xiàn)剛才的代碼
async function init() {
const [user, info] = await Promise.all([
getUser(),
getInfo()
])
console.log('init', user, info)
}
現(xiàn)在這種方式,如果我們之前每個(gè)請(qǐng)求都需要1秒,一共需要2秒,那么現(xiàn)在兩個(gè)同時(shí)執(zhí)行只需要1秒就完成了!但是這樣也有一個(gè)問(wèn)題:我們并沒(méi)有考慮報(bào)錯(cuò)問(wèn)題。你可能會(huì)認(rèn)為,這個(gè)很簡(jiǎn)單,把代碼放在一個(gè)try...cahtch中不就可以了,就像這樣:
async function init() {
try {
const [user, info] = await Promise.all([
getUser(),
getInfo()
])
console.log('init ==== ', user, info)
} catch(err) {
console.log('err', err);
}
}
但是,這樣的話會(huì)有一個(gè)問(wèn)題,就像這樣:
function getUser() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('user reject')
}, 500);
})
}
function getInfo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('info reject')
}, 1000);
})
}
// 輸出 err user reject
由于getUser優(yōu)先完成并出現(xiàn)錯(cuò)誤,此時(shí)觸發(fā)了catch,而當(dāng)getInfo再次完成并出現(xiàn)錯(cuò)誤時(shí),將不會(huì)觸發(fā)catch。因?yàn)?code>catch代碼已經(jīng)運(yùn)行,函數(shù)已經(jīng)完成。 那么,要怎么做呢?接下來(lái),我們就講講應(yīng)該如何處理報(bào)錯(cuò)問(wèn)題。
如何處理報(bào)錯(cuò)
解決方式是,給Promise.all中的每個(gè)函數(shù)加上catch,如下:
function handle(err) {
console.log('err', err)
}
function onReject(err) {
handle(err);
return new Error(err);
}
async function init() {
const [user, info] = await Promise.all([
getUser().catch(onReject),
getInfo().catch(onReject)
])
console.log('init', user instanceof Error, info instanceof Error) // init true true
}
這樣,我們?cè)?code>onReject函數(shù)中處理錯(cuò)誤,并返回這個(gè)錯(cuò)誤。所以現(xiàn)在我們生成的user和info要么是Error要么是我們期望的效果,而Error我們可以用instanceof檢查它。
Promise.allSettled
解決并發(fā)我們還可以使用 Promise.allSettled ,我們會(huì)得到一個(gè)包含每個(gè)Promise結(jié)果的值或錯(cuò)誤信息。
如何實(shí)現(xiàn)
接下來(lái),我們來(lái)使用一下,代碼如下:
async function init() {
const [userStatus, infoStatus] = await Promise.allSettled([
getUser(),
getInfo()
])
console.log('info', userStatus, infoStatus)
}
現(xiàn)在,我們可以得到這樣的數(shù)據(jù):

結(jié)果對(duì)象有3個(gè)屬性:
- status:
fulfilled或rejected - value: 僅在
status為fulfilled時(shí)出現(xiàn),為Promise為resolve返回的值 - reason: 僅在
status為rejected時(shí)出現(xiàn),為Promise被reject時(shí)返回的值
因此,我們可以讀取到每個(gè)Promise的狀態(tài),并單獨(dú)處理每個(gè)錯(cuò)誤而不會(huì)遺漏任何的信息。
最后兩個(gè)技巧
Promise.race
Promise.race方法可接受一個(gè)可迭代的promise返回一個(gè)promise,一旦迭代器中某個(gè)promise解決或拒絕,返回的promise就會(huì)resolve或reject。
我們可以這樣實(shí)現(xiàn)一個(gè)簡(jiǎn)單的超時(shí)功能,代碼如下:
function getUser() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('user resolve')
}, 5100);
})
}
async function init() {
// Race to see which Promise completes first
const racePromise = Promise.race([
getUser(),
new Promise((resolve, reject) =>
// Time out after 5 seconds
setTimeout(() => reject(new Error('Timeout')), 5000)
)
])
try {
const result = await racePromise
console.log('result', result)
} catch (err) {
console.log('err', err)
// Timed out!
}
}
注意,通常情況下,如果有超時(shí),那么你需要盡量的取消未完成的待處理任務(wù)。
另外,最好還是處理所有promise的reject:
const racePromise = Promise.race([
getUser().catch(onReject),
// xxx
])
Promise.any
Promise.any 等待任何一個(gè)promise成功則為成功,只有全部的promise都被reject,才會(huì)返回reject。通常我們可以使用Promise.any來(lái)實(shí)現(xiàn),當(dāng)一個(gè)promise先完成后,取消其他的promise,不過(guò)要注意的是,我們并不總是同時(shí)要處理多個(gè)數(shù)據(jù),只是因?yàn)槲覀兛梢宰龅剑砸?jǐn)慎的使用它。
通常,我們不想出現(xiàn)未被處理的reject,所以,我們應(yīng)該這樣寫:
const anyPromise = Promise.any([
getUser().catch(onReject),
getInfo().catch(onReject)
])
總結(jié)
前面我們已經(jīng)介紹了在使用promise處理并發(fā)問(wèn)題時(shí),應(yīng)該如何的處理promise的reject。但是也需要注意:過(guò)度的并發(fā)會(huì)導(dǎo)致網(wǎng)絡(luò)抖動(dòng)、磁盤抖動(dòng)或其他問(wèn)題,雖然可以,但并不是一定要這樣做,有時(shí)使用async await也可以使代碼更容易理解和維護(hù),所以,在使用之前我們更需要考慮是否需要。
以上就是一文詳解如何有效的處理Promise并發(fā)的詳細(xì)內(nèi)容,更多關(guān)于有效處理Promise并發(fā)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于Echarts實(shí)現(xiàn)繪制立體柱狀圖的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何基于Echarts實(shí)現(xiàn)繪制立體柱狀圖的功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下2023-02-02
JavaScript中的普通函數(shù)和箭頭函數(shù)的區(qū)別和用法詳解
這篇文章主要介紹了JavaScript中的普通函數(shù)和箭頭函數(shù)的區(qū)別和用法詳解,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03
利用webpack理解CommonJS和ES Modules的差異區(qū)別
這篇文章主要介紹了利用webpack理解CommonJS和ES Modules的差異區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
JavaScript基于對(duì)象方法實(shí)現(xiàn)數(shù)組去重及排序操作示例
這篇文章主要介紹了JavaScript基于對(duì)象方法實(shí)現(xiàn)數(shù)組去重及排序操作,涉及javascript基于對(duì)象方法的數(shù)組遍歷、比較、去重、排序等相關(guān)操作技巧,需要的朋友可以參考下2018-07-07

