循环使用异步等待

在循环中使用 async await 时,你可能会遇到其中一些问题。

如果你只是尝试在 forEach 中使用 await,这将抛出 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,然后你可以像 await asyncForEach(async (e) => await somePromiseFn(e), data ) 那样基本上你会返回一个在等待和完成所有回调时解析的 promise。但是有更好的方法可以做到这一点,那就是使用循环。

你可以使用 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 调用,但是如果你希望它们同时运行,则需要在 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。我们知道这个承诺,当它解决了我们所有的值。

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

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