JavaScript 异步等待循环

示例

在循环中使用异步等待时,您可能会遇到其中一些问题。

如果您只是尝试在内部使用await forEach,则会抛出Unexpected token错误。

(async() => {
 data = [1, 2, 3, 4, 5];
 data.forEach(e => {
   const i = await somePromiseFn(e);
   console.log(i);
 });
})();

这是因为您错误地将箭头功能视为一个块。该await会在回调函数,这是不是上下文async。
解释器可以防止我们发生上述错误,但是如果您将其添加async到forEach回调中,则不会引发任何错误。您可能会认为这可以解决问题,但无法按预期工作。

例:

(async() => {
  data = [1, 2, 3, 4, 5];
  data.forEach(async(e) => {
    const i = await somePromiseFn(e);
    console.log(i);
  });
  console.log('this will print first');
})();

发生这种情况是因为回调异步函数只能暂停自身,而不能暂停父异步函数。

您可以编写一个asyncForEach函数,该函数返回一个Promise,然后您可以执行以下操作:基本上,您返回一个Promise,它在等待和完成所有回调时解析。但是有更好的方法可以做到这一点,那就是使用循环。await asyncForEach(async (e) => await somePromiseFn(e), data )

您可以使用一个for-of循环for/while,也可以使用一个循环,选择哪一个并不重要。

(async() => {
  data = [1, 2, 3, 4, 5];
  for (let e of data) {
    const i = await somePromiseFn(e);
    console.log(i);
  }
  console.log('this will print last');
})();

但是还有另一个问题。该解决方案将等待每个调用somePromiseFn完成,然后再进行下一个调用。
如果您实际上希望somePromiseFn按顺序执行调用,那么这很好,但是如果您希望它们并发运行,则需要awaiton Promise.all。

(async() => {
 data = [1, 2, 3, 4, 5];
 const p = await Promise.all(data.map(async(e) => await somePromiseFn(e)));
 console.log(...p);
})();

Promise.all接收一个promises数组作为其唯一参数,并返回一个promise。当数组中的所有promise被解析后,返回的promise也将被解析。我们await信守这一诺言,当它成为现实时,我们所有的价值观都可以实现。

上面的示例是完全可运行的。该somePromiseFn函数可以作为具有超时的异步回显函数使用。您可以尝试至少使用stage-3预设的babel-repl中的示例,并查看输出。

function somePromiseFn(n) {
 return new Promise((res, rej) => {
   setTimeout(() => res(n), 250);
 });
}