JavaScript 异步任务控制

javascript_async_patterns2026-10-29 11:03:47

JavaScript 异步任务控制关注多个操作的启动、结果聚合、并发上限、取消与过期结果处理。Promise 表示结果,并不自动提供线程、限流或底层操作取消。

聚合结果

  • Promise.all:全部成功才兑现,任一拒绝就使聚合 Promise 拒绝,结果数组按输入顺序排列。
  • Promise.allSettled:等待全部结束,为每项保留 fulfilled 或 rejected 结果。
  • Promise.race:采用第一个落定的结果,可能成功也可能失败。
  • Promise.any:采用第一个成功结果,全部失败才以 AggregateError 拒绝。

all 与 race 的聚合结果提前结束,不会自动中止其他已启动操作。all 与 allSettled 的空输入结果可成功,race 的空输入会持续待定,any 的空输入会拒绝。

参考:Promise 官方参考。

限制并发

任务应保存为尚未启动的函数。先 map(fetch) 再分组 await,无法限制已经发出的请求。

以下示例接收有限任务数组,保留输入结果顺序;首个任务失败后,阻止尚未启动的任务继续启动,但不取消已运行任务:

async function runLimited(tasks, limit) {
  if (!Number.isInteger(limit) || limit < 1) {
    throw new RangeError("limit 必须是正整数");
  }
  const results = new Array(tasks.length);
  let cursor = 0;
  let stopped = false;

  async function worker() {
    while (!stopped) {
      const index = cursor++;
      if (index >= tasks.length) return;
      try {
        results[index] = await tasks[index]();
      } catch (error) {
        stopped = true;
        throw error;
      }
    }
  }
  await Promise.all(
    Array.from({ length: Math.min(limit, tasks.length) }, worker)
  );
  return results;
}

示例假定每项都是返回值或 Promise 的函数。限并发不等于每秒限流;需要单位时间请求配额时还要控制启动节奏。对于部分失败仍需继续的任务,应在单项内记录失败并采用明确的结果结构。

取消与过期结果

AbortController 可为支持 signal 的 fetch 等操作发出取消信号。它不会回滚服务器已经完成的写入,也不会自动取消任意 Promise。

搜索场景还可为每次请求分配递增序号,只允许最新请求更新数据、错误和 loading。取消旧请求节省资源,忽略旧结果保证界面状态正确,两者可配合使用。

参考:AbortController 官方参考。

重复提交与刷新登录态

经验上,提交按钮的进行中状态用于避免意外重复操作;真正需要防止重复写入时,应由服务端保证相应幂等语义。请求超时不代表服务端一定未执行。

多个请求同时需要刷新登录态时,可复用同一个进行中的刷新 Promise;刷新成功后按明确规则重试,失败则统一退出。必须限制重试次数,并避免刷新请求再次进入同一刷新循环。

本页目录