壹 ❀ 引
我在 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,我們依舊能拿到完整的響應結果。那么到這里本文結束。
參考