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