本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
参考文章
p-limit作用
p-limit 用于限制node的并发数。
为什么要限制并发?
node.js支持高并发,但是对于被node调用的供应商来说,高并发是负担,是资源占用,需要限制在合理范围内。
p-limit实现关键
类似于我们在疫情期间排队等待做核酸一样,concurrency是采样医生的个数,activeCount是正在被采样的人的数量,quenue是等待采样的排队队伍,run函数类似于我们张嘴“啊”在采样, next函数是我们采样完成之后离开。
- activeCount统计正在执行的函数数量
- queue队列存入等待执行的函数
- run函数执行从queue队列出来的函数
源码:
import Queue from 'yocto-queue';
/**
* 限制并发
* @param {*} concurrency 正整数
* @returns
*/
export default function pLimit(concurrency) {
// 限制入参只能为正整数
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
throw new TypeError('Expected `concurrency` to be a number from 1 and up');
}
// 建立一个队列
const queue = new Queue();
// 计数器,统计当前正在执行的函数数量
let activeCount = 0;
// 出队列
const next = () => {
// 执行完成了,计数器减一
activeCount--;
if (queue.size > 0) {
// 队列的第一个出队并执行run函数
queue.dequeue()();
}
};
// 执行异步函数
const run = async (fn, resolve, args) => {
// 计数器加一
activeCount++;
const result = (async () => fn(...args))();
resolve(result);
try {
await result;
} catch {}
next();
};
const enqueue = (fn, resolve, args) => {
// bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
// 传入null/undefined的时候将执行js全局对象浏览器中是window,其他环境是global
// 排队等候
queue.enqueue(run.bind(undefined, fn, resolve, args));
(async () => {
// This function needs to wait until the next microtask before comparing
// `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
// when the run function is dequeued and called. The comparison in the if-statement
// needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
// 此函数需要等到下一个微任务后再将“activeCount”与“concurrency”进行比较,
// 因为当 run函数取消排队并调用时,“activeCount”会异步更新。
// if 语句中的比较也需要异步进行,以获取“activeCount”的最新值。
await Promise.resolve();
// 如果计数器的数量小于传入的并发数,并且还有需要执行的请求,那么则执行队伍的第一个请求
if (activeCount < concurrency && queue.size > 0) {
// 队列的第一个出队并执行run函数
queue.dequeue()();
}
})();
};
const generator = (fn, ...args) => new Promise(resolve => {
// 进入对列
enqueue(fn, resolve, args);
});
Object.defineProperties(generator, {
activeCount: { // 获取当前正在执行的异步并发数量
get: () => activeCount,
},
pendingCount: { // 返回排队等候的函数的数量
get: () => queue.size,
},
clearQueue: { // 清空队列
value: () => {
queue.clear();
},
},
});
return generator;
}