源码阅读07-p-limit

简介: 并发场景在所有的开发过程中都是会遇到的。像面向对象语言 C#、Java 经常都是使用多线程的技术来解决并发的场景。但是 Node 是单线程的,对于解决并发基本是通过异步方式。还有使用 node 提供的 Cluster 模块来完成多进程的。注意点:Node 中的异步是为了解决不堵塞主线程,而不是为了解决并发问题。

源码地址:https://github.com/sindresorhus/p-limit

前言

并发场景在所有的开发过程中都是会遇到的。像面向对象语言 C#、Java 经常都是使用多线程的技术来解决并发的场景。但是 Node 是单线程的,对于解决并发基本是通过异步方式。还有使用 node 提供的 Cluster 模块来完成多进程的。

注意点:Node 中的异步是为了解决不堵塞主线程,而不是为了解决并发问题。

Promise.all

Promise.all 应该是很多人都会使用到的并发问题的解决,以及其他的 Promise.series 等。Promise.all 最后的结果是所有的异步执行后的结果集合。

Promise.all([...]).then(result=>{
  ....
})

但当并发数量超过一定的数量时可能就会导致下游的承受能力不足以支撑你想要并发的数量。就想数据库连接池,如果开启太多的话一个可能造成浪费,另一个原因可能就是数据库没法提供那么大的连接数让你使用。所以我们也要限制并发的数量。

p-limit

对于 Promise.all 如果只是少量的,完全可以支持。但是超量时该如何限制并发数,这里 P-limit 结合 Promise 就完美解决这个问题。

const fns=[....]; // 方法集合

const limit = pLimit(10)
Promise.all(fns.map(fn=> limit(async()=>{
  await fn()
})))

从上面的源码可以看出其还是在使用 Promise.all 处理多个函数的执行,每个函数还是异步执行。但注意每个要执行的函数都是在 limit方法内,执行 limit(fn) 会在调用 fn后返回一个 promise 。当然在执行前会先通过 plimit 获取一个 limit方法。

再深入 plmit 的源码里,limit如何控制并发数。

首先获取一个 limit 方法时会初始化一个队列。使用 limit(fn)时由内部的 generator 函数生成的。它其实就是将函数压入队列内。可以认真观察当 activeCount<concurrency && queue.size <0 时就会从队列取出函数并执行它。

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 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;
}

所以整个过程其实就是在内部使用了一个队列和活跃数量 activeCount 来控制,无论多少个需要执行的并发,都会先放入队列中再判断正在执行的任务数量是否小于并发数,如果小于并发数就可以从队列中取出执行。每个函数都是异步执行,执行玩就从队列中获取下一个任务继续执行。

接下来就是操作实践。写了几个简单的方法然后执行结果,即便我方法有很多个也不受影响。

目录
相关文章
|
SQL 关系型数据库 MySQL
总结 vue3 的一些知识点:MySQL 连接的使用
总结 vue3 的一些知识点:MySQL 连接的使用
|
Serverless Python
Python实现布林线策略
使用Python实现布林线策略案例的简单示例
396 2
|
机器学习/深度学习 缓存 人工智能
大语言模型中常用的旋转位置编码RoPE详解:为什么它比绝对或相对位置编码更好?
Transformer的基石自2017年后历经变革,2022年RoPE引领NLP新方向,现已被顶级模型如Llama、Llama2等采纳。RoPE融合绝对与相对位置编码优点,解决传统方法的序列长度限制和相对位置表示问题。它通过旋转矩阵对词向量应用角度与位置成正比的旋转,保持向量稳定,保留相对位置信息,适用于长序列处理,提升了模型效率和性能。RoPE的引入开启了Transformer的新篇章,推动了NLP的进展。[[1](https://avoid.overfit.cn/post/9e0d8e7687a94d1ead9aeea65bb2a129)]
1861 0
|
小程序 前端开发 API
一文就知道uniapp等跨端开发的使用场景,学习成本,如何快速使用,基本语法等
uniapp是一个跨平台开发各种各样应用的一套框架。只需要写一套代码,可以适配多达14种产品类型,比如H5移动端、微信小程序及各种其他小程序,ios、安卓等接近原生APP的应用(可以上架到App Store或应用商店)。所以这里的多端,指的并不是PC、平板、手机端,而是移动端优先,开发者可以一次编码,分别编译为小程序和 Android 以及 iOS 应用,实现多端开发
833 0
|
12月前
|
机器学习/深度学习 编解码 自然语言处理
ResNet(残差网络)
【10月更文挑战第1天】
|
消息中间件 存储 NoSQL
MQ的顺序性保证:顺序队列、消息编号、分布式锁,一文全掌握!
【8月更文挑战第24天】消息队列(MQ)是分布式系统的关键组件,用于实现系统解耦、提升可扩展性和可用性。保证消息顺序性是其重要挑战之一。本文介绍三种常用策略:顺序队列、消息编号与分布式锁,通过示例展示如何确保消息按需排序。这些方法各有优势,可根据实际场景灵活选用。提供的Java示例有助于加深理解与实践应用。
764 2
|
开发工具 git Windows
【Git】‘git‘ 不是内部或外部命令,也不是可运行的程序
【Git】‘git‘ 不是内部或外部命令,也不是可运行的程序
|
并行计算 API 计算机视觉
Python多线程与多进程:概念、区别及应用场景解析
Python多线程与多进程:概念、区别及应用场景解析
513 0
|
人工智能 安全 数据安全/隐私保护
团队协作工具Slack介绍
团队协作工具Slack介绍
935 0