核心思路是限制同时执行的promise数量,等有任务完成后再启动新任务;通过“池”式调度实现动态并发控制,避免全量并发或完全串行。

在 JavaScript 中控制异步任务的并发数量,核心思路是:**限制同时执行的 Promise 数量,等有任务完成后再启动新任务**。不靠 `Promise.all` 全量并发,也不靠串行逐个等待,而是用“池”(pool)的方式动态调度。
用 PromisePool 管理并发数
最直接的方式是使用轻量级库(如 p-limit),它封装了并发控制逻辑:
- 安装:
npm install p-limit - 用法示例(最多同时运行 3 个 fetch 请求):
import pLimit from 'p-limit'; const limit = pLimit(3); const urls = ['/api/1', '/api/2', '/api/3', '/api/4', '/api/5']; const promises = urls.map(url => limit(() => fetch(url).then(r => r.json()))); const results = await Promise.all(promises); // 按顺序返回结果
注意:limit(fn) 返回的是一个包装后的函数,调用它才会入队;真正执行受控于内部队列和空闲槽位。
手写简易并发控制器(不依赖库)
理解原理后,可自己实现一个最小可用版本:
- 维护一个运行中任务计数器和待执行队列
- 每次有任务完成,就从队列取下一个启动
- 用 async/await + 闭包保持状态干净
function createTaskPool(max = 3) {
let running = 0;
const queue = [];
return function push(taskFn) {
const runner = () => taskFn().finally(() => {
running--;
if (queue.length > 0) queue.shift()();
});
if (running fetch(`/data/${i}`).then(r => r.json()))
.then(data => results.push(data));
}
这个版本不保证结果顺序,适合只关心最终全部完成的场景。若需保序,可在每个任务里记录索引再归并。
配合 Promise.allSettled 或 reduce 实现可控批处理
对数组分批处理也是常见策略,适合已知任务列表且希望逻辑清晰:
- 把长数组切分成每组 N 项
- 每组用
Promise.allSettled并发执行 - 用
reduce串行推进各批次(避免内存爆炸)
async function runInBatches(tasks, batchSize = 3) {
const batches = [];
for (let i = 0; i fn()));
results.push(...batchResults);
}
return results;
}
这种方式易于调试、内存友好,也方便加失败重试或超时控制。
关键细节提醒
实际使用时容易忽略但影响很大的点:
-
错误处理要明确:
Promise.all遇错即停,推荐用allSettled或 try/catch 包裹单个任务 -
取消能力需额外支持:原生 Promise 不支持取消,如需中断,得结合
AbortController或用可取消的库(如 p-cancelable) - 避免“假并发”:如果任务本身是 CPU 密集型(如大数组排序),Node.js 单线程下并发并不能提速,反而增加调度开销
- 浏览器环境注意连接数限制:Chrome 对同一域名默认最多 6 个 HTTP/1.1 连接,高并发请求可能排队,HTTP/2 可缓解
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











