背景
最近遇到一个async/await 在forEach下不是串行执行的问题, 代码如下,一开始很自信是一秒输出一个(串行执行),依次输出1,2,3, 结果现实狠狠的打脸(好疼), 结果是1秒后输出1,2,3(并行执行)。 一脸懵逼.
const waitOneSecond = (i) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(i)
}, 1000)
})
}
[1, 2, 3].forEach(async (i) => {
const res = await waitOneSecond(i)
console.log(res)
})
forEach中的async/await为什么不是串行执行
既然 async/await 在for of遍历中是能串行执行的,那么问题肯定是出在forEach, 先看看forEach的实现方式, 在MDN上找到 forEach的polyfill实现方式。
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(callback, thisArg) {
var T, k;
if (this == null) {
throw new TypeError(' this is null or not defined');
}
var O = Object(this);
var len = O.length >>> 0;
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function');
}
if (arguments.length > 1) {
T = thisArg;
}
k = 0;
// 重点是使用while循环
while (k < len) {
var kValue;
if (k in O) {
kValue = O[k];
callback.call(T, kValue, k, O);
}
k++;
}
};
}
简化后
while (k < len) {
callback.call(T, O[k], k, O);
k++;
}
forEach的第一个参数是回调函数,我们可以看到forEach内部使用 了while循环执行了回调函数, 所以最后会导致回调函数是并行执行的。
如果对于上述还是不理解为什么forEach中的sync/await是并行执行的,我们可以这样理解对于forEach来说, while循环会后展开成三个独立的async, await。独立的async下的await是并行执行的.
(async () => {
const res = await waitOneSecond(1)
console.log(res)
})();
(async () => {
const res = await waitOneSecond(2)
console.log(res)
})();
(async () => {
const res = await waitOneSecond(3)
console.log(res)
})();
只有async下对应的全部await才是串行执行的,
(async () => {
const res1 = await waitOneSecond(1)
console.log(res1)
const res2 = await waitOneSecond(2)
console.log(res2)
const res3 = await waitOneSecond(3)
console.log(res3)
})();
解决办法
1.对forEach加上 async/await(改原始的函数不是一种好的方式)
Array.prototype.forEach = async function(callback, thisArg) {
while (k < len) {]
// await回调函数就可以串行执行了
await callback.call(T, O[k], k, O);
k++;
}
}
- 使用for of 代替 forEach(
推荐)。这样就能保证async/await是个串行执行的了。