Skip to content

JavaScript中的异步数组循环

异步操作似乎让很多开发者感到困惑。当与数组循环结合时,会有一些注意事项需要注意。

for循环

asyncfor(或for...of)循环结合起来可能是执行异步操作的最直接的选项之一。在for循环中使用await会导致代码停止并等待异步操作完成后再继续执行。这意味着所有的Promise将按顺序运行。

const asyncUppercase = item =>
  new Promise(resolve =>
    setTimeout(
      () => resolve(item.toUpperCase()),
      Math.floor(Math.random() * 1000)
    )
  );

const uppercaseItems = async () => {
  const items = ['a', 'b', 'c'];
  for (item of items) {
    const uppercaseItem = await asyncUppercase(item);
    console.log(uppercaseItem);
  }

  console.log('Items processed');
};

uppercaseItems();
// 输出: 'A', 'B', 'C', 'Items processed'

Promises

Promise.all()提供了另一种在数组上进行异步循环的选项。与前面的选项相比,主要区别在于Promise.all()会并行执行所有的异步操作。这意味着Promise将无序执行,这在某些情况下可能会成为问题。大多数情况下,这是我首选的解决方案,因为很少有需要按顺序执行Promise的情况。

const asyncUppercase = item =>
  new Promise(resolve =>
    setTimeout(
      () => resolve(item.toUpperCase()),
      Math.floor(Math.random() * 1000)
    )
  );

const uppercaseItems = () => {
  const items = ['a', 'b', 'c'];
  return Promise.all(
    items.map(async item => {
      const uppercaseItem = await asyncUppercase(item);
      console.log(uppercaseItem);
    })
  ).then(() => {
    console.log('Items processed');
  });
};
// 输出: 'A', 'C', 'B', 'Items processed'

数组方法

不幸的是,Array.prototype.forEach() 等数组方法与 async/await 不兼容。唯一可行的解决方案是使用 Promise.all(),如前面的示例所示。使用带有 Array.prototype.forEach()async 回调将导致代码的其余部分执行,而异步操作不会被等待。

const asyncUppercase = item =>
  new Promise(resolve =>
    setTimeout(
      () => resolve(item.toUpperCase()),
      Math.floor(Math.random() * 1000)
    )
  );

const uppercaseItems = async () => {
  const items = ['a', 'b', 'c'];
  await Promise.all(items.map(async item => {
    const uppercaseItem = await asyncUppercase(item);
    console.log(uppercaseItem);
  }));

  console.log('Items processed');
};

uppercaseItems();
// 输出: 'Items processed', 'B', 'A', 'C'