Promise.all 并发限制
1. 为什么要限制并发?
Promise.all 可以保证 promises 数组中所有 promise 都达到 resolve 状态,才执行 then 回调。
但如果 promises 数组每个对象都是 http 请求或复杂调用,数量达到几十万个:
- 瞬间发出几十万 http 请求(TCP 连接数不足可能造成等待)。
- 堆积无数调用栈导致内存溢出。
这时就需要对 Promise.all 做并发限制——每个时刻并发执行的 promise 数量固定,最终执行结果与 Promise.all 保持一致。
Promise 并非因为调用
Promise.all才执行,而是在实例化时就开始执行。要做并发限制,只能从实例化上下手,把生成 promises 数组的控制权交给并发控制逻辑。
2. 实现思路
- 通过第三方插件控制异步函数的并发执行。
- 自行实现:定义
PromisePool对象,先把并发池塞满,再通过Promise.race获取池中任务完成的信号,从任务队列里取新任务塞入。
3. 简单实现
class PromisePool {
constructor(max, fn) {
this.max = max; // 最大并发量
this.fn = fn; // 自定义请求函数
this.pool = []; // 并发池
this.urls = []; // 剩余请求
}
start(urls) {
this.urls = urls;
while (this.pool.length < this.max) {
const url = this.urls.shift();
this.setTask(url);
}
return this.run(Promise.race(this.pool));
}
run(race) {
race.then(() => {
const url = this.urls.shift();
this.setTask(url);
return this.run(Promise.race(this.pool));
});
}
setTask(url) {
if (!url) return;
const task = this.fn(url);
this.pool.push(task);
console.log(`${url} 开始,当前并发数:${this.pool.length}`);
task.then(() => {
this.pool.splice(this.pool.indexOf(task), 1);
console.log(`${url} 结束,当前并发数:${this.pool.length}`);
});
}
}
const URLS = [
'bytedance.com', 'tencent.com', 'alibaba.com',
'microsoft.com', 'apple.com', 'hulu.com', 'amazon.com'
];
const requestFn = (url) => new Promise((resolve) => {
setTimeout(() => resolve(`任务 ${url} 完成`), 1000);
}).then((res) => console.log('外部逻辑', res));
const pool = new PromisePool(5, requestFn);
pool.start(URLS);
4. 现代方案
p-limit:Node 端小巧实用的并发限制库。p-queue:基于 Promise 的并发队列,支持优先级。async-pool:轻量实现。
const limit = require('p-limit');
const pool = limit(5);
await Promise.all(URLS.map((url) => pool(() => requestFn(url))));
来源整理自:我的有道云笔记



