精讀《用 Reduce 實現 Promise 序列執行》
本週精讀的文章是ofollow,noindex">why-using-reduce-to-sequentially-resolve-promises-works ,講了如何利用 reduce 實現 Promise 序列執行。
在 async/await 以前 Promise 序列執行還是比較麻煩的,希望根據這篇文章可以理清楚序列 Promise 的思維脈絡。
2 概述
除了依賴async
promise-fun
等工具庫,最常用的佇列操作就是Array.prototype.reduce()
了:
let result = [1, 2, 5].reduce((accumulator, item) => { return accumulator + item; }, 0); // <-- Our initial value. console.log(result); // 8 複製程式碼
最後一個值 0 是起始值,每次 reduce 返回的值都會作為下次 reduce 回撥函式的第一個引數,直到佇列迴圈完畢,因此可以進行累加計算。
那麼將reduce
的特性用在 Promise 試試:
function runPromiseByQueue(myPromises) { myPromises.reduce( (previousPromise, nextPromise) => previousPromise.then(() => nextPromise), Promise.resolve() ); } 複製程式碼
當上一個 Promise 開始執行(previousPromise.then
),當其執行完畢後再呼叫下一個 Promise,並作為一個新 Promise 返回,下次迭代就會繼續這個迴圈。
const createPromise = (time, id) => new Promise(solve => setTimeout(() => { console.log("promise", id); solve(); }, time) ); runPromiseByQueue([ createPromise(3000, 1), createPromise(2000, 2), createPromise(1000, 3) ]); 複製程式碼
得到的輸出是:
promise 1 promise 2 promise 3 複製程式碼
3 精讀
Reduce
是同步執行的,在一個事件迴圈就會完成(更多請參考Javascript%2520%25E4%25BA%258B%25E4%25BB%25B6%25E5%25BE%25AA%25E7%258E%25AF%25E4%25B8%258E%25E5%25BC%2582%25E6%25AD%25A5%25E3%2580%258B.md" rel="nofollow,noindex">精讀《Javascript 事件迴圈與非同步》
),但這僅僅是在記憶體快速構造了 Promise 執行佇列,展開如下:
new Promise((resolve, reject) => { // Promise #1 resolve(); }) .then(result => { // Promise #2 return result; }) .then(result => { // Promise #3 return result; }); // ... and so on! 複製程式碼
Reduce
的作用就是在記憶體中生成這個佇列,而不需要把這個冗餘的佇列寫在程式碼裡!
更簡單的方法
感謝eos3tion
同學補充,在 async/await 的支援下,runPromiseByQueue
函式可以更為簡化:
async function runPromiseByQueue(myPromises) { for (let value of myPromises) { await value(); } } 複製程式碼
多虧了 async/await,程式碼看起來如此簡潔明瞭。
不過要注意,這個思路與reduce
思路不同之處在於,利用reduce
的函式整體是個同步函式,自己先執行完畢構造 Promise 佇列,然後在記憶體非同步執行;而利用 async/await 的函式是利用將自己改造為一個非同步函式,等待每一個 Promise 執行完畢。
更多 Promise 拓展
天豬 同學分享的promise-fun 除了序列 Promise 解決方案,還提供了一系列 Promise 功能拓展(有一些逐漸被 ES 標準採納,比如finally 已經進入 Stage 4),如果你的專案還無法使用 async/await,是不需要自己重新寫一遍的,當然本文的原理還是需要好好理解。
Stage 相關可以進行拓展閱讀ECMAScript%2520%25E6%258F%2590%25E6%25A1%2588.md" rel="nofollow,noindex">精讀《TC39 與 ECMAScript 提案》 。
4 總結
Promise 序列佇列一般情況下用的不多,因為序列會阻塞,而使用者互動往往是並行的。那麼對於並行發請求,前端按序列順序接收 Response 也是一個有意思的話題,留給大家思考。