- 本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- 这是源码共读的第31期,链接:【若川视野 x 源码共读】第31期 | p-limit 限制并发数 - 掘金 (juejin.cn)。
1.p-limit介绍
Run multiple promise-returning & async functions with limited concurrency
以有限的并发运行多个异步函数,高并发会占用过多资源,影响其他进程。
2.p-limit使用
import pLimit from 'p-limit';
const limit = pLimit(1);
const input = [
limit(() => fetchSomething('foo')),
limit(() => fetchSomething('bar')),
limit(() => doSomething())
];
// Only one promise is run at once
const result = await Promise.all(input);
console.log(result);
3.p-limit代码
import Queue from "yocto-queue";
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");
}
// 建立一个队列,Queue为链表
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生成一个新的函数,this指向为undefined
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`.
await Promise.resolve();
//若当前执行的异步任务个数小于并发限制的数量,继续执行
if (activeCount < concurrency && queue.size > 0) {
queue.dequeue()();//执行run()
}
})();
};
const generator = (fn, ...args) =>
new Promise((resolve) => {
// 将异步函数放入队列
enqueue(fn, resolve, args);
});
//监听generator里面的属性
Object.defineProperties(generator, {
activeCount: {
get: () => activeCount,
},
pendingCount: {
get: () => queue.size,
},
clearQueue: {
value: () => {
queue.clear();
},
},
});
//返回一个promise 函数
return generator;
}
4.promise.all方法
function (values) {
// promise 以第一个结果为准,其它的逻辑还是走,只是不采纳了
return new Promise((resolve, reject) => {
// 并发是循环 串行递归
let arr = [];
let times = 0;
function processData(index, data) {
arr[index] = data;
if (++times === values.length) {
resolve(arr);
}
}
for (let i = 0; i < values.length; i++) {
let cur = values[i];
Promise.resolve(cur).then((data) => {
processData(i, data);
}, reject);
}
});
};