异步函数的并发控制p-limit

474 阅读2分钟

「我正在参与掘金会员专属活动-源码共读第一期,点击参与

本篇文章介绍的是异步函数的并发控制。

有的时候,我们在进入页面的时候发起请求数量不只一个

  • 如何控制并发数量呢?
  • 还有怎么获取这些异步编程的状态?等待的和正在运行的各有几个?
  • 如何将等待的请求能够手动取消掉?

这时候p-limit就登场了。

安装

 npm install p-limit

使用

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);
  • pLimit设置并发控制的数量,返回一个limit对象。
  • limit可以获取等待的异步函数数量和已经执行的异步函数数量
  • limit同时还可以传入异步函数及若干个参数放入异步队列(对应源码的queue)中。

源码

源码路径根目录/index.js

解析源码

  1. 主函数在源码的第三行,传入的参数是1以及以上的整数
  2. 通过yocto-queue创建了一个队列queue
  3. 声明了两个函数nextrunnext是做activeCount递减和出队的操作。run是用来执行异步函数和activeCount递增的。
  4. enqueue作用是将传入的异步函数通过run进行包裹的并放入队列,然后会等待一个微任务执行完毕之后,在activeCount小于并发数量并且队列长度大于0的情况下出队操作。等待微任务执行完毕的目的是为了等待activeCount更新。
  5. generator函数返回一个Promise对象,里面包裹的是enqueue函数
  6. 最后通过defineProrpertiesgenerator批量增加属性:activeCount,pendingCount,clearQueue

回顾一下:

const enqueue = (fn, resolve, args) => {
		queue.enqueue(run.bind(undefined, fn, resolve, args));
		(async () => {
                        // 下面这段代码其实还是很巧妙的,通过模拟一个微任务,能得到`activeCount`的最新值,因为`run`方法本身是一个异步函数。如果直接获取`activeCount`可能是还没执行异步函数之前的值。
			await Promise.resolve();

			if (activeCount < concurrency && queue.size > 0) {
				queue.dequeue()();
			}
		})();
	};

结束语

这段源码利用了yocto-queue的队列数据结构来作为异步函数的存储。如果有合适的库,引用进来,对于我们实现自己的逻辑还是很方便的。所以认识优秀的库更多,对我们的帮助更大。