什麼是Promise串列
- 2020 年 2 月 25 日
- 筆記
什麼是Promise串列
Promise串列是指每一個由promise封裝的任務都順序執行,即上一個執行完成後再執行下一個。
執行過程大致是下面的樣子:
Task A | ------>| Task B | ------>| Task C | ------>| Task D | ------>|
為什麼要講Promise串列
我們平時會比較多的使用並行,即多個任務一起執行,也就是利用Promise.all()
。但其實在日常開發中串列也是會遇到的,比如依次開啟走廊的所有燈,或者其次讓噴泉的水槍依次噴水等等。但是ES6中的Promise
並沒有對串列進行直接封裝,所以需要我們自己來做。

img
分布講解Promise串列
Promise串列習題
之前有小夥伴發給過我一道這樣的面試題,所以本文準備通過這道題來實現一下Promise串列。
定義
type Task = () => Promise
(即 Task 是一個 類型,是一個返回值是 Promise 的函數類型) 假設有一個數組 tasks: Task[](每一項都是一個 Task 類型的數組) 實現一個方法function execute(tasks: Task[]): Promise
,該方法將 tasks 內的任務 依次 執行,並返回一個結果為數組的 Promise ,該數組包含任務執行結果(以執行順序排序) 要求:忽略異常任務,並在結果數組中用 null 佔位 限制:不添加任何依賴,僅使用 Promise,不使用 Generator 或 async
如果允許使用Generator
或者async/await
來寫的話,會很簡單,文章末尾再實現async/await
的方法。
先做完成一下測試用例的程式碼:
const Task = (result, isSuccess = true) => { return () => new Promise((resolve, reject) => { setTimeout(() => { if (isSuccess) { console.log(`success: ${result}`); resolve(result); } else { console.log(`error: ${result}`); reject(result); } }, 1000); }); } execute([ Task('A'), Task('B'), Task('X', false), Task('C'), ]).then(resultList => { // 這裡期望列印 ["A", "B", null, "C"] console.log(resultList) })
思路大致如下圖:先做一個Promise
實例,然後把每個Task
循環的放置到上一個promise
的then
回調里。

需要注意的幾點:
- 無論每個Task是成功還是失敗,它都不能阻斷下一個Task的執行
- 最後的then需要把每個Task的執行結果"決議"出去
對策:
- 每一個Task外層包裝一層Promise,捕獲Task的reject狀態
- 可以利用一個中間變數,快取所有Task的輸出結果,然後在最後一個Promise的then里把中間變數「決議」出去
第一版程式碼如下:
function execute(tasks) { let resultList = []; return tasks.reduce( (previousPromise, currentPromise) => previousPromise.then((resultList) => { return new Promise(resolve => { currentPromise().then(result => { resultList.push(result); resolve() }).catch(() => { resultList.push(null); resolve(resultList.concat(null)) }) }) }), Promise.resolve() ).then(() => resultList); }
改進
其實Promise的鏈式操作是可以傳遞值的,所以可以利用這個特性,省去中間變數,
程式碼如下:
function execute(tasks) { return tasks.reduce( (previousPromise, currentPromise) => previousPromise.then((resultList) => { return new Promise(resolve => { currentPromise().then(result => { resolve(resultList.concat(result)) }).catch(() => { resolve(resultList.concat(null)) }) }) }), Promise.resolve([]) ) }
aysnc/await版本
程式碼如下:
const execute = async (tasks = []) => { const resultList = []; for(task of tasks) { try { resultList.push(await task()); } catch (e) { resultList.push(null); } } return resultList; }