重新思考 JavaScript 中的异步循环:从并发控制到顺序执行的实践指南

19 阅读1分钟

Rethinking async loops in JavaScript - Matt Smith

循环中的如何发送请求,示例代码使用 TrasHTTPandas - Raccoon HTTP Responses 接口

const requestData = async (id) => {
  const res = await fetch(`/api/json/${id}`);
  if (!res.ok) {
    throw new Error(`HTTP 错误!状态:${String(res.status)}`);
  }
  const data = await res.json();
  return data;
};

接口调用相互独立

不依赖其他的接口的返回值

使用 Promise.all

map 函数内部就处理错误

如果不在内部处理错误,fetchUser(2) 抛出错误(例如 404 或网络错误),则整个 Promise.all 调用将被拒绝,并且不会返回任何结果(包括成功的结果)。

const request = async () => {
  const users = ["200", "123", "200"];

  const results = await Promise.all(
    users.map(async (id) => {
      try {
        return await requestData(id);
      } catch (err) {
        console.error(`Failed to fetch user ${id}`, err);
        return { id, name: "Unknown User" };
      }
    })
  );
  console.log(results);
};

使用 Promise.allSettled

const request = async () => {
  const users = ["200", "123", "200"];

  const results = await Promise.allSettled(users.map((id) => requestData(id)));

  results.forEach((result) => {
    if (result.status === "fulfilled") {
      console.log("✅ User:", result.value);
    } else {
      console.warn("❌ Error:", result.reason);
    }
  });
};
## 控制并发数量

```js
import pLimit from "p-limit";

const request = async () => {
  const limit = pLimit(1);
  const users = ["200", "123", "200"];
  const limitedFetches = users.map((id) => limit(() => requestData(id)));
  const results = await Promise.allSettled(limitedFetches);

  results.forEach((result) => {
    if (result.status === "fulfilled") {
      console.log("✅ User:", result.value);
    } else {
      console.warn("❌ Error:", result.reason);
    }
  });
};

接口之间有依赖关系

  • 顺序执行
  • 接口频率限制或者批处理
  • 独立请求速度较慢

for 循环中使用 await

const request = async () => {
  const users = ["200", "123", "200"];

  for (const id of users) {
    const user = await fetchUser(id);
    console.log(user);
  }
};

for of

for (const id of users) {
  const user = await fetchUser(id);
  console.log(user);
}

使用 Array.fromAsync

const request = async () => {
  const users = ["200", "123", "200"];

  Array.fromAsync(users, async (id) => {
    const user = await requestData(id);
    console.log(user);
  });
};

错误的写法

map 函数里面使用 await

返回一个 Promise 数组,拿不到真实的数据

const request = async () => {
  const users = ["200", "123", "200"];

  const results = users.map(async (id) => {
    const user = await fetchUser(id);
    return user;
  });

  console.log(results);
};

不要在 forEach 中使用 await

循环不会等待您的异步函数,无法保证完成时间和顺序。

users.forEach(async id => {
  const user = await fetchUser(id);
  console.log(user); 
});