p-limit,优雅地实现并发

什么是并发?

并发,顾名思义,就是一起发生。而在前端中,并发大多是与请求挂钩的。

并发的场景

在前端开发中,难免要遇到并发请求的场景,比如说对大文件进行分片上传,文件有100M,而每个分片只有1M,因此要发送100次请求。

大家都知道浏览器是有并发限制的,一次性只允许发送6个请求,如果一次性发送100个请求,肯定会造成卡顿,此外,如果这时候除了文件上传的请求,还有其他请求,也会造成不好的体验,因此我们有必要手动对请求进行并发限制。

在github上有很多实现并发请求的库。p-limit就是一个非常优秀的实现并发请求的库,接下来,让我们来深入了解一下这个库的源码,学习一下如何优雅地实现并发。

源码

p-limit主要是通过队列的形式将所有任务推入到队列中,只有当前执行数量少于并发数且队列不为空时才会从队列中取出任务执行。

生成Promise实例

javascript 复制代码
export default function pLimit(concurrency) {
    const queue = new Queue();
    let activeCount = 0;
    const generator = (fn, ...args) => new Promise(resolve => {
        enqueue(fn, resolve, args);
    });
        .....
 }

首先p-limit的队列用的是yocto-queue这个库,主要用到了入队和出队的方法。 concurrency 就是并发数,activeCount 则是当前执行的任务数。 函数generator用于生成一个新的Promise实例,将请求任务Promise的状态改变函数resolve 以及剩余参数传入到enqueue函数中。

enqueue入队

scss 复制代码
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()();
        }
    })();
};

enqueue函数中,调用yocto-queue的enqueue方法,将经过run调用bind方法创建新函数推入到队列中。

下面的async/await立即执行函数 则是为了确保比较是在队列中的请求任务执行之后进行获取到最新的任务执行数,如果少于并发数的话且队列不为空,则从队列中取出任务执行。

run执行

ini 复制代码
const run = async (fn, resolve, args) => {
    activeCount++;
    const result = (async () => fn(...args))();
    resolve(result);
    try {
        await result;
    } catch {}
    next();
};

run函数就是用来执行请求任务的,activeCount++,当前执行的任务数加一。获取到请求的结果,通过generator函数传递过来的resolve改变它生成的Promise的状态。

这里async/await的作用是等待当前任务执行完再执行下个任务,next函数也就是用于执行下一个任务的。

next执行下一个任务

ini 复制代码
const next = () => {
    activeCount--;
    if (queue.size > 0) {
        queue.dequeue()();
    }
};

当执行next的时候,说明上一个任务已经执行完毕了,因此将activeCount减一,同时如果队列不为空,取出一个任务来执行。

其他属性

至此p-limit的核心代码已经讲解完毕,除此之外,p-limit还额外提供了三个属性,分别是activeCountpendingCountclearQueue

css 复制代码
Object.defineProperties(generator, {
    activeCount: {
            get: () => activeCount,
    },
    pendingCount: {
            get: () => queue.size,
    },
    clearQueue: {
            value: () => {
                    queue.clear();
            },
    },
});

其中activeCount表示当前执行的任务数 、pendingCount表示等待执行的任务数 ,他们都是只读 的,不允许被修改,而clearQueue这个属性则可以用来清除队列中未执行的请求,这三个属性拓展了p-limit的功能,让它变得更为强大。

除此之外,为了提升一定的容错,p-limit还对传入的并发数进行的判断,需要满足如下条件:是数字、不能为Infinity、且必须大于0,如果不满足的话,则会抛出错误。

javascript 复制代码
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
    throw new TypeError('Expected `concurrency` to be a number from 1 and up');
}

总结

以上就是关于p-limit解读的全部内容啦,这仅是我在学习p-limit源码时的理解,如果哪里有误欢迎大佬指出,以后我也会分享更多关于源码的解读!

相关推荐
十五_在努力几秒前
参透 JavaScript —— 异步编程与Promise
前端·javascript·promise
顾林海3 分钟前
Flutter 图片组件全面解析:从基础加载到高级应用
android·前端·flutter
背书包的甜瓜4 分钟前
使用sass 实现px转vh或vw,适配适老化时,在设计图字体大小的基础上,增加3px(可配置),
前端·css·sass
十五_在努力5 分钟前
参透 JavaScript —— 图解 Event Loop 事件循环
前端·javascript
十五_在努力12 分钟前
参透JavaScript —— 判断数据类型的四种方式
前端·javascript
依辰18 分钟前
小程序自动化构建与版本管理方案优化
前端·javascript·微信小程序
Geoffwo18 分钟前
取消echarts地图悬浮时默认黄色高亮
前端·javascript·echarts
前端大卫27 分钟前
🔥 如何“为所欲为”地渲染页面:优雅拦截 Fetch 和 XMLHttpRequest!
前端·javascript
苏州第一深情27 分钟前
【vue+leaflet】自定义控件(五)
前端·javascript·vue.js
逾明28 分钟前
在项目中使用Volta控制node版本
前端·node.js