理解Promise.all,Promise.all與Promise.race的區別,如何讓Promise.all在rejected失敗后依然返回resolved成功結果


 壹 ❀ 引

我在 es6入門4--promise詳解 這篇文章中有詳細介紹Promise對象的用法,文章主題更偏向於對於Promise概念的理解與各方法基本使用介紹;而世上一個比較有趣的問題就是,即便按照前人提供的規則與方法去做一件事,也會因為未知的緣故產生新問題,這讓人非常苦惱,但大多數情況都是因為自身理解不深刻導致;在昨天的工作中使用Promise.all的經歷也是把我整的不輕(最后查出來是后台邏輯BUG...),這也是我想另起一篇文章專門介紹Promise.all與Promise.race的原因。

那么本文主要圍繞三個觀點展開,一是重新認識Promise.all與Promise.race,二是理解而二者有本質區別,三是在all方法失敗的情況下如何依然獲取resolve的狀態,那么本文開始。

 貳 ❀ Promise.all

Promise.all用於將多個Promise實例包裝成一個新的Promise實例,我們來看個例子:

var p1 = Promise.resolve(1);
var p2 = Promise.resolve(2);
var p = Promise.all([p1,p2]);
console.log(p);

上述例子中新Promise實例 p 的回調結果受p1 p2影響,即如果p1,p2都resolved,p的成功回調會執行,並返回一個包含p1 p2 resolved結果的數組

p.then(function (resp) {
    console.log(resp);//[1,2]
})

但如果p1,p2其中任意一個rejected,p的失敗回調會執行,並返回第一個失敗的結果,成功回調不執行

var p1 = Promise.resolve(1);
var p2 = Promise.reject(2);
Promise.all([p1, p2])
    .then(function (resp) {
        console.log(resp); //不會執行
    }).catch(function (err) {
        console.log(err); //2
    });

需要注意的是,如果Promise.all()的參數狀態未確定未確定,那么在Promise.all的回調執行會先調用Promise.resolve方法(或者reject方法)將這些pending狀態的Promise實例轉為明確的狀態;更有趣的是,轉過執行雖然符合JS執行機制先后順序,但轉變完成后all的成功回調拿到的數組結果順序,永遠與Promise參數順序一致,我們來看個例子:

var p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1);
        console.log(1);//后執行
    }, 4000);
})
var p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2);
        console.log(2);//先執行
    }, 1000);
})
Promise.all([p1, p2])
    .then(function (resp) {
        console.log(resp); //[1,2]
    })

在上述例子中,由於p1,p2不是一個Promise實例,所以在all回調前還得執行resolve方法,由於兩者定時器等待時間不同,所以時間短的先執行,但即便如此,all回調的結果,p1的結果依然在前:

叄 ❀ Promise.all與Promise.race的區別

Promise.race同樣是將多個Promise實例包裝成一個新的Promise實例,但新實例的執行結果與第一個先改變狀態的Promise狀態保持一致,即如果第一個Promise實例為rejected,那么新實例也為rejected,反之亦如此。

var p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1);
        console.log(1); // 第三個執行
    }, 4000);
})
var p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(2);
        console.log(2); //先執行
    }, 1000);
})
Promise.race([p1, p2])
    .then((resp) => {
        console.log(resp);//不執行
    }).catch((err) => {
        console.log(err);//2 第二個執行
    })

在上述例子中,由於p2先執行,狀態為rejected,導致race的catch方法執行執行,拿到錯誤的響應。

那么到這里我們大概知道了兩者的不同,Promise.all就像執行任務的刺客團隊,要么全部成功才算成功,要么一個失敗直接失敗,非常講究團隊精神。而Promise.race的結果只參照第一個改變狀態的Promise,第一個為成功它就成功,第一個失敗它就跟着失敗,非常冷酷無情。

其次,Promise.all的resolveed結果順序與參數順序完全一致,即便第一個參數改變狀態在后,但它的結果依舊在前,Promise.all與Promise.race轉變參數狀態的順序都符合JS執行機制,以定時器為例,時間小的永遠先改變狀態。

 肆 ❀ Promise.all在reject后依舊返回resolve

在上文中,我們知道Promise.all在處理多個Promise實例時,如果一個失敗,就只能拿到第一個失敗的結果,其余成功的結果都無法拿到,那有什么辦法能在reject后依舊拿到所有執行結果呢?有,利用catch方法。

首先我們需要知道Promise的狀態具有可傳遞性,其次catch方法在執行后也會返回一個狀態為resolved的新Promise實例,所以我們只要將可能reject的Promise實例先catch一遍就可以了,就像做一次狀態預加工:

var p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1);
    }, 1000);
});
var p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(2);
    }, 1000);
});

var promiseArr = [p1, p2];
var promiseArr_ = promiseArr.map(function (promiseItem) {
    return promiseItem.catch(function (err) {
        return err;
    })
});

Promise.all(promiseArr_)
    .then((resp) => {
        console.log(resp);//[1,2]
    }).catch((err) => {
        console.log(err);
    });

 伍 ❀ 總

那么到這里,我們重新介紹了Promise.all與Promise.race這兩個方法,通過本文你也知道了all的特點是要么參數全部成功則自己成功,要么某個失敗則自己失敗,而race只跟隨第一個改變狀態的Promise執行對應回調;其次我們還利用了catch方法完善了Promise.all方法,即便有參數rejected,我們依舊能拿到完整的響應結果。那么到這里本文結束。

 參考

Promise.all 處理error

理解和使用Promise.all和Promise.race


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM