Bibby Chung
my-coding-life
Published in
4 min readFeb 22, 2018

--

分批非同步執行(Execute Batch of Promises)

from web..

分批執行這個議題,其實之前就有想要寫了,只是每次完成需求後,就覺得好累,然後就忘記這件事...最近有朋友又提起,覺得這次不寫,下次一定就又忘了…

直接主題,在客戶的 spec 裡,「大量 server 端呼叫 api」一直是常常出現的需求,如…「3萬筆客戶的簡訊通知」「1萬筆 EDM 的傳送」「10萬筆 excel 的匯入」,這些看起來好像就簡單的需求,在程式設計師的眼裡,根本就是如臨大敵,因為衍生需求,真的會嚇到吃手手...

那啥是衍生需求呢?如:「客戶名單哪裡來」「我要搜尋我要的客戶」「我可以幫我記起來我之前的客戶嗎」「我要看執行的進度」「如果有問題,我要手動暫停」「我要可以自己設定執行的時間」「如果錯誤了,我要可以從我上次失敗的地方繼續」「可以 10 萬筆在 1 分鐘內發完嗎」「我要看執行後的報表細節」…等等,如果你是程式設計師,如果您在估時間的時候,沒有把這些衍生需求估進去,我想你只能躲到角落哭哭了…

那今天會把這些「需求」都說出來嗎?答案是不會,因為說完這些需求,命應該只剩半條了 XD…那本篇要說啥?我會寫個 simple code 來討論分批執行的一些觀念,那先貼 code 吧!

import * as _ from "lodash";const testRun = async () => {  // fake data
const arr = [];
for (let i = 0; i < 50; i += 1) {
arr.push(i);
}
// run promise
const runPromise = async (i: number) => {
console.log(i);
return Promise.resolve(i);
}
// separate into batch data
const batchNumber = 3;
const batchArr = _.chain(arr).chunk(batchNumber).value();
// execute
for (const batch of batchArr) {
const pArr = [];
for (const item of batch) {
pArr.push(runPromise(item));
}
await Promise.all(pArr);
console.log(`--- break line ---`)
}
}testRun();

上面的 code 很少,但是有幾個很重要的觀念…

  1. Promise 是 new 出來就馬上執行,不是 .then 才執行,所以要執行時候才去 new 出來才對,一般都會用 method 包起來,或是用柯里化(curry)去處理,這個觀念很重要
  2. 分批的方式,有很多種寫法,lodash 提供很簡單的方法 .chunk 幫你分好分滿
  3. Promise.all 是同步全部執行,等所有的結果完成後才跑 .then

我們可以把上面這一段 code 包裝一下,之後重複執行這簡單的分批執行功能了...

import * as _ from "lodash";async function batchRunner<T>(data: T[], batchNumber: number, fun: (obj: T) => Promise<void>) {  // seporate into batch array
const batchData = _.chain(data).chunk(batchNumber).value();
// execute
for (const batch of batchData) {
const pArr = [];
for (const item of batch) {
pArr.push(fun(item));
}
await Promise.all(pArr);
console.log(`--- break line ---`)
}
}
const testRun = async () => { // fake data
const arr: number[] = [];
for (let i = 0; i < 50; i += 1) {
arr.push(i);
}

// execute
batchRunner(arr, 3, async (i: number) => {
console.log(i);
return Promise.resolve();
});
}testRun();

大家可以去試試看,有任何問題都可以一起討論喔!

--

--