遇到 async,別用 forEach

Steven Chen
4 min readFeb 20, 2020

--

遇到 async,別用 forEach
Photo by Paweł Bukowski on Unsplash

開始之前,請打開我在 codepen上準備的範例:
Editor / Full Page

用 forEach來處理異步(asynchronous)的情況會怎樣?

多點幾次範例上的 ForEach按鈕,你會發現每次輸出 1st, 2nd跟 3rd的順序都不一樣。

ForEach

這種難以事先預測結果的狀態,就是異步的 race condition

為什麼會這樣?

先看一下 mozilla文件上對 forEach的描述:

arr.forEach(function callback(currentValue[, index[, array]]) {
//your iterator
}[, thisArg]);

這是因為 forEach並不會在乎 callback function是不是 async functrion(也就是他的 return是不是一個 promise)、也不會等到 promise被 resolve或 reject才執行下一次迴圈。

你可以把 forEach想像成是:

Array.prototype.forEach = function (callback) {
// this represents our array
for (let index = 0; index < this.length; index++) {
// We call the callback for each entry
callback(this[index], index, this);
}
};

可以看到 function前面沒有 async、 callback前面也沒有 await

這樣就算傳入的是一個 async function也沒有用:

texts.forEach(async text => {
const res = await addSuffix(text);
console.log(res);
})

網路上常見的做法是自己寫一個 asyncForEach:

async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}

或者也可以改用 for/for-in/for-of再自己加上 async/await:

for(let i = 0; i < texts.length; i += 1) {
const res = await addSuffix(texts[i]);
console.log(res);
}
for (let i in texts) {
const res = await addSuffix(texts[i]);
console.log(res);
}
for (let text of texts) {
const res = await addSuffix(text);
console.log(res);
}
for/for-in/for-of

參考資料:
JavaScript: async/await with forEach() by Sebastien Chopin

--

--