前端知库

怎样实现有明确失败语义的异步并发池?

参考答案

限制并发需要接收尚未执行的任务函数,而不是已经开始的 Promise。下面的教学实现接收函数数组,最多同时等待 limit 个任务,结果按输入顺序排列。这里限制的是异步在途任务,不会让 JavaScript 计算自动多线程并行。

JavaScript
async function runPool(tasks, limit) {
  if (!Array.isArray(tasks)) throw new TypeError('任务必须是数组');
  if (!Number.isInteger(limit) || limit < 1) {
    throw new RangeError('并发数必须为正整数');
  }
  const queue = Array.from(tasks);
  if (queue.some(task => typeof task !== 'function')) {
    throw new TypeError('每个任务都必须是函数');
  }
  const results = new Array(queue.length);
  let next = 0;
  let failed = false;
  async function worker() {
    while (!failed && next < queue.length) {
      const index = next++;
      try {
        results[index] = await queue[index]();
      } catch (error) {
        failed = true; // 得知失败后停止领取新任务
        throw error;
      }
    }
  }
  await Promise.all(Array.from(
    { length: Math.min(limit, queue.length) },
    () => worker(),
  ));
  return results;
}

同步抛错或异步拒绝都会使整体拒绝。得知失败前已经启动的任务仍会继续,整体拒绝不表示它们已经停止;需要取消时,应另行设计 AbortSignal 协作。示例不包含重试、超时、动态加任务或每秒限流,生产需求要分别定义这些策略。

代码为本项目教学实现,参考文档用于核对 Promise 语义,不是官方提供的并发池。