JavaScript并发控制与Promise高级用法

并发控制通过限制同时执行的任务数防止资源过载,常用asyncPool实现;结合Promise.allSettled处理全部结果、Promise.race设置超时、优先级队列调度,提升异步任务的稳定性与效率。

JavaScript中的并发控制是处理多个异步任务时避免资源争用、提升性能和稳定性的重要手段。尤其是在需要批量请求接口或处理大量Promise时,直接并发执行所有任务可能导致服务器压力过大或浏览器卡顿。通过合理的并发控制策略,可以有效管理异步任务的执行节奏。结合Promise的高级用法,能更灵活地实现复杂的异步流程控制。

并发控制的基本原理

并发控制的核心是在多个异步任务中限制同时运行的任务数量。常见的做法是使用“信号量”机制或任务队列来管理执行状态。

例如,实现一个并发数为n的请求控制器:

function asyncPool(poolLimit, array, iteratorFn) {
  const ret = [];
  const executing = [];
  let i = 0;

  const enqueue = () => {
    if (i >= array.length) return Promise.resolve();

    const item = array[i++];
    const p = Promise.resolve().then(() => iteratorFn(item));
    ret.push(p);

    const e = p.then(() => executing.splice(executing.indexOf(e), 1));
    executing.push(e);

    if (executing.length >= poolLimit) {
      return Promise.race(executing).then(enqueue);
    }

    return enqueue();
  };

  return Promise.all(ret);
}

这个函数允许最多poolLimit个任务同时执行,其余任务等待空位释放后再启动,从而实现可控的并发。

Promise.allSettled与错误容忍

在并发场景中,有时不希望某个Promise失败导致整个流程中断。Promise.all会在任意一个Promise拒绝时立即抛出错误,而Promise.allSettled会等待所有Promise完成(无论成功或失败),并返回结果数组。

适用于需要收集全部结果的场景,比如批量上传文件:

  • 每个上传操作返回一个Promise
  • 使用Promise.allSettled统一处理结果
  • 分别判断每个结果的状态进行后续操作
Promise.allSettled(uploads).then(results => {
  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      console.log(`文件${index}上传成功`, result.value);
    } else {
      console.error(`文件${index}上传失败`, result.reason);
    }
  });
});

利用Promise.race实现超时控制

在并发任务中,某些请求可能因网络问题长时间挂起。可以通过Promise.race为每个任务添加超时机制。

定义一个超时包装函数:

function withTimeout(promise, ms) {
  const timeout = new Promise((_, reject) => {
    setTimeout(() => reject(new Error('Request timeout')), ms);
  });
  return Promise.race([promise, timeout]);
}

将其应用于具体请求:

const request = fetch('/api/data');
withTimeout(request, 5000).catch(err => console.log(err.message));

这样可防止个别慢请求拖累整体性能。

动态调度与优先级控制

更高级的并发控制可引入优先级队列。例如,将任务按重要性分级,高优先级任务优先执行。

可通过维护多个任务队列,并在空闲时按优先级拉取新任务来实现:

  • 使用数组存储不同优先级的任务组
  • 每次有执行位置空出时,从最高优先级非空队列中取任务
  • 结合async/await简化调度逻辑

这种模式适合复杂应用如爬虫、消息系统等。

基本上就这些。掌握并发控制和Promise的组合使用,能让异步代码更加健壮高效。关键在于理解Promise的状态流转以及如何通过组合原生方法实现所需行为。不复杂但容易忽略细节。