我正在参与掘金会员专属活动-源码共读第一期,点击参与
前言
看到标题,我们先来想想什么是并发?其实从某种程度上来说,并发有点像同步,可以理解为同时进行,同时发生。比如说有两个任务,通过不断切换这两个任务来完成两个任务,这种情况就叫做并发。那么,为啥要限制并发呢?因为计算机的并处理能力都是有限的,为了使得处理有序,因此有必要限制同时处理请求的数量。所以今天我们来学习一下p-limit
限制并发数的知识。
p-limit
p-limit
使用pLimit
来定义并发数,返回一个函数用于接收每一个异步任务,保证无论传入多少个异步任务,都始终根据传入的并发数量来进行执行。了解了它的相关概念,我们再来看看关于它的使用方法吧。下面这是官方的使用示例。
import pLimit from 'p-limit';
const limit = pLimit(1);
const input = [
limit(() => fetchSomething('foo')),
limit(() => fetchSomething('bar')),
limit(() => doSomething())
];
const result = await Promise.all(input);
console.log(result);
从上面不难看出,关于它的使用方式还是比较简单的,清晰易懂。
源码解读
我们现在来看看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');
}
const queue = new Queue();
let activeCount = 0;
const next = () => {
activeCount--;
if (queue.size > 0) {
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) => {
queue.enqueue(run.bind(undefined, fn, resolve, args));
(async () => {
await Promise.resolve();
if (activeCount < concurrency && queue.size > 0) {
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;
}
源码中引入了一个我们都比较熟悉的东西 —— yocto-queue
,因此我们要有队列和链表的基础,关于yocto-queue
的学习可以看看这篇文章 数组还是队列?yocto-queue 源码告诉你。
虽然比较少,但是整体看下来代码会有点繁杂。首先它是定义了一个队列queue
,然后里面又定义来一个异步函数run
。在run
函数之前有一个next
函数,它的作用是减少当前并发数,并判断是否移出队列中的任务。任务如何移出呢?通过queue.dequeue()()
来实现。
任务被移出后,开始执行异步函数run
,内部进行一系列操作后在下一个任务中又执行next
函数。
总结
今天学习的p-limit
源码,虽然代码不多,但是里面的逻辑却不少,需要多看几遍来消化消化。我们学习的可能不止是一个p-limit
,也学习到了同步异步以及这些async
函数的执行顺序。所以需要多理解一下,里面知识点还是挺多的。