async/await
使用起来非常棒,但是在Array.forEach()
中却存在陷阱。
问题描述
例如说下面的代码:
const waitFor = (ms) => new Promise(r => setTimeout(r, ms));
[1, 2, 3].forEach(async (num) => {
await waitFor(1000);
console.log(num);
});
console.log('Done');
在控制台执行上面代码结果如下(node.js版本≥ 13.0.0):
$ node forEach.js
$ Done
$ 1
$ 2
$ 3
问题分析
console.log(num)
的结果在最后才输出,并且是同时输出。
我们创建一个forEach()
方法理解下到底发生了什么事:
Array.prototype.forEach = function (callback) {
// 这里迭代我们的数组
for (let index = 0; index < this.length; index++) {
// 每次迭代执行 callback 方法,传入对应参数
callback(this[index], index, this);
}
};
forEach()
的 polyfill 实现可以参考:MDN-Array.prototype.forEach()
可以看到,callback
并没有在async/await
中执行,所以Promise
的结果在最后才打印。
解决问题
我们可以用自己写的asyncForEach
方法代替forEach
:
asyncForEach([1, 2, 3], async (num) => {
await waitFor(50);
console.log(num);
})
console.log('Done');
执行上面的代码,可以看到下面的结果:
$ node forEach.js
$ Done
$ 1
$ 2
$ 3
结果好像没什么区别,不过console.log(num)
已经在async/await
中执行了,我们也可以看到setTimeout
的效果。
但是还不够理想,实际上asyncForEach
返回的是一个Promise
,因为它包裹在一个async
函数中,我们可以等待它执行完成后再打印出最后的Done
:
const start = async () => {
await asyncForEach([1, 2, 3], async (num) => {
await waitFor(50);
console.log(num);
});
console.log('Done');
}
start();
再次执行可以看到正确的结果:
$ node forEach.js
$ 1
$ 2
$ 3
$ Done
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。