1. 程式人生 > >精讀《用 Reduce 實現 Promise 序列執行》

精讀《用 Reduce 實現 Promise 序列執行》

1 引言

在 async/await 以前 Promise 序列執行還是比較麻煩的,希望根據這篇文章可以理清楚序列 Promise 的思維脈絡。

2 概述

除了依賴 async promise-fun 等工具庫,最常用的佇列操作就是 Array.prototype.reduce() 了:

JavaScript
12345 let result=[1,2,5].reduce((accumulator,item)=>{returnaccumulator+item;},0);// <-- Our initial value.console.log(result);// 8

最後一個值 0 是起始值,每次 reduce 返回的值都會作為下次 reduce 回撥函式的第一個引數,直到佇列迴圈完畢,因此可以進行累加計算。

那麼將 reduce 的特性用在 Promise 試試:

123456 functionrunPromiseByQueue(myPromises){myPromises
.reduce((previousPromise,nextPromise)=>previousPromise.then(()=>nextPromise()),Promise.resolve());}

當上一個 Promise 開始執行(previousPromise.then),當其執行完畢後再呼叫下一個 Promise,並作為一個新 Promise 返回,下次迭代就會繼續這個迴圈。

12345678910111213 constcreatePromise=(time,id)=>()=>newPromise(solve=>setTimeout(()=>{console.log("promise",id);solve();},time));runPromiseByQueue([createPromise(3000,1),createPromise(2000,2),createPromise(1000,3)]);

得到的輸出是:

123 promise1promise2promise3

3 精讀

Reduce 是同步執行的,在一個事件迴圈就會完成(更多請參考 精讀《Javascript 事件迴圈與非同步》),但這僅僅是在記憶體快速構造了 Promise 執行佇列,展開如下:

123456789101112131415 newPromise((resolve,reject)=>{// Promise #1resolve();}).then(result=>{// Promise #2returnresult;}).then(result=>{// Promise #3returnresult;});// ... and so on!

Reduce 的作用就是在記憶體中生成這個佇列,而不需要把這個冗餘的佇列寫在程式碼裡!

更簡單的方法

感謝 eos3tion 同學補充,在 async/await 的支援下,runPromiseByQueue 函式可以更為簡化:

12345 async functionrunPromiseByQueue(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,是不需要自己重新寫一遍的,當然本文的原理還是需要好好理解。

4 總結

Promise 序列佇列一般情況下用的不多,因為序列會阻塞,而使用者互動往往是並行的。那麼對於並行發請求,前端按序列順序接收 Response 也是一個有意思的話題,留給大家思考。

5 更多討論

如果你想參與討論,請點選這裡,每週都有新的主題,週末或週一釋出。前端精讀 – 幫你篩選靠譜的內容。