示例说明
对 500 个 ID 直接 Promise.all 会同时发起 500 个请求,容易触发限流或耗尽句柄。p-limit 返回一个限流器函数,所有任务先进入其内部队列,同一时刻只有 N 个真正执行,后续任务在完成一个后自动补位。既保留了并发的吞吐收益,又把资源占用控制在常数级别。
技术要点
- 所有任务先进队列,固定 N 个并发执行
- Promise.all 仍然等待全部任务完成
- 限流器函数本身是可复用的单一实例
代码解读
syncAll 中每个 fetch 都包在 limit 回调里,map 立即创建全部包装任务但不并发执行,Promise.all 拿到的仍是最终结果数组,顺序与输入一致。第二段用数组长度模拟活跃计数,直观展示并发数始终不超过 2 的补位过程。
使用建议
并发数应结合下游配额与本机资源设定,CPU 密集任务可设为核数。若任务可能失败,配合 Promise.allSettled 收集全部结果而不是整体中断。多个限流器不要互相嵌套,容易造成死锁。
代码示例
import pLimit from 'p-limit';
const limit = pLimit(4);
async function syncAll(ids) {
const tasks = ids.map((id) =>
limit(() => fetch(`https://api.example.com/items/${id}`).then((r) => r.json())),
);
const results = await Promise.all(tasks);
return results;
}
const started = [];
const tracker = pLimit(2);
const jobs = [1, 2, 3, 4].map((n) =>
tracker(async () => {
started.push(n);
console.log('running', n, 'active:', started.length);
await new Promise((r) => setTimeout(r, 100));
started.pop();
}),
);
await Promise.all(jobs);