遇到 async,別用 forEach
4 min readFeb 20, 2020
開始之前,請打開我在 codepen上準備的範例:
Editor / Full Page
用 forEach來處理異步(asynchronous)的情況會怎樣?
多點幾次範例上的 ForEach按鈕,你會發現每次輸出 1st, 2nd跟 3rd的順序都不一樣。
這種難以事先預測結果的狀態,就是異步的 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);
}
參考資料:
JavaScript: async/await with forEach() by Sebastien Chopin