发布于 2026-01-06 0 阅读
0

JavaScript 中的异步循环——使用 forEach、map 和 for 循环

JavaScript 中的异步循环——使用 forEach、map 和 for 循环

在开发 Web 应用程序时,异步操作至关重要。我们都认同 async/await 极大地简化了我们处理异步操作的工作。

在这篇文章中,我们将学习如何将循环与 async/await 结合使用。

在深入探讨之前,这里列出我在演示中使用的实用函数。


// creates a logger function to print logs with function name
function getLogger(fnName) {
  return function logger(value, diffInMS) {
    return console.log(
      `${fnName}: Item ${value} finished waiting ${Math.round(
        diffInMS / 1000
      )} seconds later.`
    );
  };
}

// simulates an async flow, a network request for example
async function waitFor(seconds) {
  // used to create the fancy waterfall
  fetch("https://random-data- 
         api.com/api/stripe/random_stripe" + Math.random());

  // the fake asynchronous task
  return new Promise((resolve, reject) => {
    setTimeout(resolve, seconds * 1000);
  });
}

Enter fullscreen mode Exit fullscreen mode

经典的 For 循环

const list = [1, 2, 3, 4, 5, 6, 7, 8 , 9, 10];

export async function mainWithFor() {
  const start = Date.now();
  const logger = getLogger("mainWithFor");
  for (const item of list) {
    await waitFor(2);
    const later = Date.now();
    logger(item, later - start);
  }
}
Enter fullscreen mode Exit fullscreen mode

按顺序逐一运行代码,等待每一行代码waitFor执行完毕后再进行下一次迭代。

下图展示了一个漂亮的瀑布图,请注意每个绿色部分都是在前一个部分开始两秒后开始的。(不必在意它们的持续时间,因为结束点是随机的。这只是为了展示瀑布图的效果。)

瀑布循环

你还会注意到,日志会以 2 秒的间隔逐一出现。

console-logs-sequence-for

这种方法的一个好应用场景是执行顺序操作,即希望前一个操作完成后再执行下一个操作。


forEach 高阶方法

export async function mainWithForEach() {
  const start = Date.now();
  const logger = getLogger("mainWithForEach");
  list.forEach(async (item) => {
    await waitFor(2);
    const later = Date.now();
    logger(item, later - start);
  });
}

Enter fullscreen mode Exit fullscreen mode

这个forEach循环与另一个循环的行为不同for,另一个for循环await每次迭代都会执行一次,而这个forEach循环则会同时执行所有迭代。因此,所有十次执行都从同一点开始,并在 2 秒后记录日志。

console-logs-forEach-循环

我们还可以使用瀑布图来观察这一点,看看它们是如何同时开始的。(再次提醒,请忽略每次执行的持续时间,这是随机 API 的结果。)

瀑布式 forEach 循环

这种方法的一个典型应用场景是并行操作,无需关心前一个操作是否完成。它比for循环快得多。但这种方法也有一个缺点:如果请求的 API 有某种速率限制机制,那么同时发出多个请求可能会适得其反。


映射高阶方法

export async function mainWithMap() {
  const start = Date.now();
  const logger = getLogger("mainWithMap");
  const promises = list.map(async (item) => {
    await waitFor(2);
    const later = Date.now();
    logger(item, later - start);
  });
  const finalAnswer = await Promise.all(promises)
}
Enter fullscreen mode Exit fullscreen mode

map函数在异步操作方面表现得与异步操作完全相同forEach,这意味着所有回调同时开始,并在 2 秒后准确记录日志。

此外,它.map还会返回一个 promise 数组(每次执行返回一个 promise,顺序相同)。

之后我们可以执行一个操作await Promise.all(promises)来从中获取最终答案数组。

需要注意的是,如果输入数组中的 Promise 哪怕只有一个被拒绝,Promise.all 也会完全被拒绝。

map应该在需要根据每个异步操作返回一些数据的地方使用。如果不需要返回数据,那么继续使用默认方法也forEach未尝不可。


如果您想亲自尝试一下,这里是codesandbox的链接。

我还制作了一个YouTube 视频,对本文内容进行了实际操作讲解。


希望您阅读这篇文章时和我写作时一样感到愉快!

想了解更多此类文章,请在推特上关注我。

下次再见

文章来源:https://dev.to/ans human_bhardwaj/asynchronous-loops-in-javascript-using-foreach-vs-map-vs-for-loop-5020