在 JavaScript 中控制异步并发操作量(即限制同时执行的异步任务数量)是优化性能和资源管理的关键。以下是多种实现方法及其详细说明:
一、使用 Promise
和队列控制并发
通过自定义队列系统限制并发数,逐步执行任务。
代码实现
async function asyncPool(poolLimit, tasks) {const executing = new Set(); // 记录正在执行的任务const results = [];for (const task of tasks) {// 如果当前并发数已达上限,等待任意一个任务完成if (executing.size >= poolLimit) {await Promise.race(executing);}const p = task().then(result => {executing.delete(p); // 任务完成后从执行队列移除results.push(result);return result;});executing.add(p); // 将任务加入执行队列}// 等待所有剩余任务完成await Promise.all(executing);return results;
}// 使用示例
const tasks = [() => fetch('url1'),() => fetch('url2'),// ...更多任务
];
asyncPool(3, tasks).then(results => {console.log('所有任务完成:', results);
});
核心逻辑
- 使用
Set
记录正在执行的任务。 - 当并发数超过限制时,通过
Promise.race()
等待任意一个任务完成。 - 任务完成后更新队列并记录结果。
二、第三方库 p-limit
使用轻量级库 p-limit
快速实现并发控制。
安装与使用
npm install p-limit
import pLimit from 'p-limit';const limit = pLimit(3); // 最大并发数 3const tasks = [limit(() => fetch('url1')),limit(() => fetch('url2')),// ...更多任务
];// 等待所有任务完成
Promise.all(tasks).then(results => {console.log(results);
});
优点
- 代码简洁,无需手动管理队列。
- 支持动态添加任务。
三、分批次执行(Batch Processing)
将任务拆分成多个批次,逐批执行。
代码实现
async function batchProcess(tasks, batchSize) {const results = [];for (let i = 0; i < tasks.length; i += batchSize) {const batch = tasks.slice(i, i + batchSize);const batchResults = await Promise.all(batch.map(task => task()));results.push(...batchResults);}return results;
}// 使用示例
const tasks = [() => fetch('url1'),() => fetch('url2'),// ...更多任务
];
batchProcess(tasks, 3).then(results => {console.log('所有批次完成:', results);
});
缺点
- 前一批次全部完成后才会执行下一批次,资源利用率较低。
四、基于 Promise
的手动调度
通过递归或循环动态启动任务。
代码实现
function controlledConcurrency(tasks, concurrency) {let index = 0;const results = [];// 递归执行任务const runTask = async () => {if (index >= tasks.length) return;const taskIndex = index++;const task = tasks[taskIndex];try {results[taskIndex] = await task();} catch (error) {results[taskIndex] = error;}await runTask(); // 继续执行下一个任务};// 启动初始的并发任务const workers = Array.from({ length: Math.min(concurrency, tasks.length) },() => runTask());return Promise.all(workers).then(() => results);
}// 使用示例
controlledConcurrency(tasks, 3).then(results => {console.log('所有任务完成:', results);
});
五、使用 async-pool
库
专为并发控制设计的库,支持灵活配置。
安装与使用
npm install async-pool
import asyncPool from 'async-pool';const timeout = (ms) => new Promise(resolve => setTimeout(resolve, ms));async function run() {const tasks = Array.from({ length: 10 }, (_, i) => () => timeout(1000).then(() => i));const results = await asyncPool(3, tasks);console.log(results); // [0, 1, 2, 3, ...]
}run();
六、浏览器环境中的 Promise.withResolvers()
(ES2024+)
利用最新的 JavaScript 特性简化代码。
async function controlledConcurrency(tasks, concurrency) {const results = [];const queue = [...tasks];const workers = Array.from({ length: concurrency }, async () => {while (queue.length) {const task = queue.shift();const { promise, resolve, reject } = Promise.withResolvers();try {const result = await task();results.push(result);resolve(result);} catch (error) {reject(error);}await promise;}});await Promise.all(workers);return results;
}
总结
方法 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
手动队列控制 | 需要精细控制逻辑 | 灵活,无依赖 | 代码复杂度高 |
p-limit |
快速实现并发控制 | 简洁,社区维护 | 需安装第三方库 |
分批次执行 | 简单分批任务 | 实现简单 | 资源利用率低 |
手动调度 | 动态任务管理 | 灵活控制任务启动顺序 | 需处理递归逻辑 |
async-pool |
需要现成解决方案 | 功能丰富 | 需安装第三方库 |
根据需求选择:
- 简单场景:使用
p-limit
或分批次执行。 - 精细控制:手动队列或递归调度。
- 最新特性:ES2024 的
Promise.withResolvers()
。