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源码时的理解,如果哪里有误欢迎大佬指出,以后我也会分享更多关于源码的解读!

相关推荐
m0_748247552 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
m0_748255022 小时前
前端常用算法集合
前端·算法
真的很上进3 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203983 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
NiNg_1_2343 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
如若1234 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~4 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语4 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport4 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap
brrdg_sefg4 小时前
WEB 漏洞 - 文件包含漏洞深度解析
前端·网络·安全