异步并发控制pLimit 实现总结
异步并发控制pLimit 实现总结
一、核心概念
1. 什么是异步并发控制?
同时发起大量异步任务(接口请求、文件读取、数据库查询)时,限制同时执行的任务数量,避免:
-
浏览器 / 服务器崩溃、请求超时、接口限流
-
资源占用过高、程序卡死
-
网络拥堵、请求失败率飙升
2. 适用场景
-
批量上传 / 下载文件
-
批量请求接口
-
批量数据库操作
-
爬虫、批量数据处理
二、核心原理
-
维护一个任务队列,存放等待执行的异步任务
-
维护一个当前正在执行的任务计数器
-
每次执行任务时:
-
计数器 < 并发数 → 立即执行
-
计数器 ≥ 并发数 → 放入队列等待
-
-
一个任务执行完成后:
-
计数器 -1
-
从队列取出一个任务继续执行
-
三、手写实现:pLimit 函数(最标准版本)
/**
* 异步并发控制器
* @param {number} concurrency - 最大并发数
* @returns {Function} limit - 包装异步任务的函数
*/
function pLimit(concurrency) {
// 校验并发数
if (!Number.isInteger(concurrency) || concurrency <= 0) {
throw new TypeError("请传入大于 0 的整数");
}
// 等待队列:存放未执行的任务
const queue = [];
// 当前正在执行的任务数量
let activeCount = 0;
// 执行下一个任务
const next = () => {
activeCount--;
// 队列有任务,取出执行
if (queue.length > 0) {
queue.shift()();
}
};
// 执行异步任务
const run = async (fn, resolve, args) => {
// 正在执行的任务 +1
activeCount++;
// 执行任务
try {
const result = await fn(...args);
resolve(result);
} catch (err) {
resolve(Promise.reject(err));
}
// 执行完,执行下一个
next();
};
// 队列添加任务
const enqueue = (fn, resolve, args) => {
// 把执行函数放进队列
queue.push(run.bind(null, fn, resolve, args));
// 没达到最大并发,立即执行
if (activeCount < concurrency) {
queue.shift()();
}
};
// 生成器:包装异步任务
const limit = (fn, ...args) => {
return new Promise((resolve) => {
enqueue(fn, resolve, args);
});
};
// 挂载状态(方便调试/扩展)
Object.defineProperties(limit, {
activeCount: { get: () => activeCount },
queueSize: { get: () => queue.length },
});
return limit;
}
四、使用示例
// 1. 创建并发控制器:最多同时执行 2 个任务
const limit = pLimit(2);
// 2. 模拟异步任务
async function task(id) {
console.log(`任务 ${id} 开始执行`);
await new Promise((r) => setTimeout(r, 1000));
console.log(`任务 ${id} 执行完成`);
return id;
}
// 3. 批量添加任务(只会同时跑 2 个)
const list = [1, 2, 3, 4, 5, 6];
const promises = list.map((item) => limit(task, item));
// 4. 等待全部完成
Promise.all(promises).then(console.log);
执行效果
任务1 开始执行
任务2 开始执行
(1秒后)
任务1 完成
任务2 完成
任务3 开始执行
任务4 开始执行
...
永远不会同时执行超过 2 个任务。
五、常用工具库(真实项目)
1. tiny-async-pool(轻量)
npm i tiny-async-pool
2. p-limit(最流行、标准)
npm i p-limit
用法和我们手写的完全一致。
六、面试考点
1. 为什么要做并发控制?
-
防止请求泛滥导致服务崩溃
-
遵守接口限流规则
-
提高稳定性、成功率
2. pLimit 核心靠什么实现?
-
队列 + 计数器
-
任务完成后自动消费下一个
-
Promise 包装异步任务
3. 手写 pLimit 关键点
-
参数校验
-
任务队列管理
-
正在执行计数器
-
任务完成自动调度
-
异常捕获不阻塞流程
七、一句话总结
异步并发控制 = 限制同时跑的异步任务数量,用队列 + 计数器实现,是批量异步任务的必备优化手段,面试高频手写题。
需要我再给你做一版超级精简面试背诵版吗?只有 10 行核心代码那种。
(注:文档部分内容可能由 AI 生成)
本文由萧兮的博客原创发布,欢迎转载,转载务必保留原文链接。
萧兮的博客:https://www.20010515.xyz · 原文:https://www.20010515.xyz/posts/019e4ece-bf14-70c3-82f3-286b8513fac9