持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
p-limit,是用来对前端并发请求进行控制的
并发是指一个应用程序中存在多个任务在执行,同时刻或者说看起来同一时刻(并发)这些任务都在执行
参考
并发控制的必要
对于浏览器来说,对于同一域名下的并发请求数量有限制,比如Chrome中只允许6个并发请求,多出来的请求只能排队,等待发送。不过在http2中同一个域名下无限制请求。
对于服务端,多个并发请求可能会对服务端产生压力。
网络请过程需要经过DNS寻址、与服务器建立连接、发送数据、等待服务器响应、接收数据这样一个漫长而复杂的过程,如等待时间过长则可能造成不好的用户体验。所以我认为控制并发数,让服务端减轻压力,让一些请求先行完成,一定程度上对用户友好。
使用
import pLimit from 'p-limit';
//设置并发限制
const limit = pLimit(1);
//用limit包裹 异步请求
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);
源码
因为用到了yocto-queue,先看这个库的用法,不知道为什么最近看到好多的彩虹小马
import Queue from 'yocto-queue';
const queue = new Queue();
//先进先出。
queue.enqueue('🦄');
queue.enqueue('🌈');
console.log(queue.size);
//=> 2
console.log(...queue);
//=> '🦄 🌈'
console.log(queue.dequeue());
//=> '🦄'
console.log(queue.dequeue());
//=> '🌈'
- index.js
按流程看,先执行pLimit函数,传入运行并行的最大数,看源码 返回 generator
再用limit包裹函数,传入。那么执行 generator函数。具体看源码
import Queue from 'yocto-queue';
export default function pLimit(concurrency) {
。。。
const queue = new Queue();
let activeCount = 0;
const next = () => {
// 执行完 任务 后,当前执行任务-1
activeCount--;
// 有任务结束去掉了,并发限制肯定没满,看看能不能加
if (queue.size > 0) {
queue.dequeue()();
}
};
const run = async (fn, resolve, args) => {
//每执行一次,当前执行任务加一
activeCount++;
// 执行fn。
const result = (async () => fn(...args))();
// 得到结果要返回
resolve(result);
try {
await result;
} catch {}
// 执行完 后 走next
next();
};
const enqueue = (fn, resolve, args) => {
// 以上面的例子举例,这里先推三个 run 任务进队里,同步加入
queue.enqueue(run.bind(undefined, fn, resolve, args));
//这里是异步任务,所以会在上面所有limit任务进队才执行。
(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`.
// 老实说我不太懂这个Promise.resolve()干嘛用的
await Promise.resolve();
// activeCount:当前执行任务数,concurrency:并发限制,队列有任务
if (activeCount < concurrency && queue.size > 0) {
// 我一开始以为是队列连续推出两个,直到我调试才知道他是队列推出一个去执行,接着看run
queue.dequeue()();
}
})();
};
// 返回promise,当promise.all启动时就执行enqueue
const generator = (fn, ...args) => new Promise(resolve => {
//这里的fn就是传入的异步请求函数。转到enqueue
enqueue(fn, resolve, args);
});
。。。
return generator;
}
其他
- 有点有趣,执行顺序从下看到上。真讨厌这样。
总结流程
1 传入并发限制最大数,返回个方法供 加异步函数
2 使用数组 组装 要限制并发的任务们 ,通过上面的方法传入 异步函数 返回 promise
3 使用promise.all 包裹上面的 队列,以便让 promise 去执行
4 执行的时候,利用同步把任务都存队列里,再通过当前执行任务数和并发限制数从队列里拿任务出来执行