p-limit源码解读:50 行代码实现并发控制

大家好,我是麦当。

p-limit 是个小而美的并发控制库,通过 50 来行代码,就实现了异步任务下的并发控制。本文我们就来研究下 p-limit 的实现方式。

关注我,一起解锁更多小而美的代码库!

前置知识

1. yotco-queue

yotco-queue是 p-limit 的作者实现的另一个库,通过链表的数据结构实现一个队列,也是同样小而美的函数,我们可以先不关注细节,只需要知道他是个队列即可,有想要了解更多的同学可以参考我这篇文章

2. #async_hooks

在源码的第二行里,会发现有个库是这样引入的

js 复制代码
import {AsyncResource} from '#async_hooks';

我们再去看看 package.json,你可以看到一个 "imports" 字段

json 复制代码
"imports": {  
    "#async_hooks": {  
        "node": "async_hooks",  
        "default": "./async-hooks-stub.js"  
    }  
},

再看看本地的 ./async-hooks-stubs.js 文件

这样做的目的是为了让这个库在不同的环境中都能正常工作。在 package.json 中,imports 字段定义了一个映射,当在项目中使用 import { AsyncResource } from '#async_hooks' 时,Node.js 环境会将其解析为内置的 async_hooks 模块,而其他环境(如浏览器)会将其解析为 ./async-hooks-stub.js 文件。这是一种常见的模式,用于处理在不同环境中可用的 API 存在差异的情况。

使用方式

首先调用 pLimit 函数,传入一个并发数,然后返回一个 limit 函数。将要执行的任务作为 limit 函数的入参传入,就会向队列里推入异步任务。

源码

解读

pLimit

一上来先 new 一个队列,然后初始化当前正在运行的个数。

generator

上文提到,调用 pLimit 后会返回一个函数,这个函数实际上就是源码中的 generator 函数。它接收一个函数,以及若干入参。generator 函数 new 一个 promise,并在 new 的过程中直接 enqueue 操作,然后返回该 promise。

enqueue

  1. AsyncResource.bind 用于确保 run 函数在执行时保持原始的异步执行上下文。这样,无论 run 函数何时被调用,或者在哪个异步上下文中被调用,它都能正确地访问到它被创建时的异步上下文。
  2. 创建完 bind 函数后,往 queue 里添加一个任务
  3. 添加完任务后,创建一个 IIFE,这个函数会在下一个微任务队列中检查当前活动的任务数量 activeCount 是否小于并发限制 concurrency,如果是,则从队列中取出一个函数并执行。

这里的 IIFE 让人挺困惑,首先,一上来就执行一个 await Promise.resolve(),这会让后续的代码都变为异步,同时因为 promise 是微任务,所以后续的代码会进入微任务队列。之所以这样做,是因为 activeCount-- 的逻辑是在 next 中进行的,所以至少要等 next 执行完成,才去进行对比,否则 activeCount 不准确。

run

run 这块说实话挺迷惑的,我自己打断点看了好几遍,因为它的异步代码写的没那么直观。

首先是 IIFE 那块,这里干了 3 件事:

  1. 定义一个异步的立即调用函数表达式(IIFE):async () => function_(...arguments_)

  2. 立即执行这个函数表达式,并将返回的 Promise 对象赋值给 result

  3. 这个 Promise 对象的解析值就是 function_ 函数的返回值。

然后是执行 resolve 函数,改变 promise 的状态。为保证 next 的顺序,通过 await 等待执行结果。当执行完成后,调用 next 函数

next

其他

总结

js 并发是常见的面试题,业务中也经常用到,研究透 p-limit,对异步任务和promise 处理的理解,会有很大提升。

相关推荐
2601_961845422 分钟前
高考真题试卷电子版|2025高考全科试卷分类下载
考研·面试·蓝桥杯·远程工作·程序员创富·高考
我叫黑大帅10 分钟前
前端如何竖屏固定视口背景
前端·javascript·面试
abcy07121312 分钟前
python pandas csv异步后台清洗前端优先返回成功信息
前端·python·pandas
折哥的程序人生 · 物流技术专研15 分钟前
《Java 100 天进阶之路》第95篇:消息队列基础(RocketMQ/Kafka)(2026版)
java·面试·kafka·rocketmq·java-rocketmq·求职招聘
不会敲代码131 分钟前
我花了三天时间,终于把 Cookie、XSS、CSRF 和浏览器存储给整明白了
javascript·面试
IT_陈寒36 分钟前
Vite这个坑我帮你踩了,动态导入居然这样才生效
前端·人工智能·后端
贩卖黄昏的熊38 分钟前
flex 布局快速梳理
开发语言·javascript·css3·html5
swipe39 分钟前
Mem0 x Agent 实战系列:分层记忆 + 三路召回,搭建真正可用的长期记忆层
前端·javascript·面试
鹤鸣的日常1 小时前
前端运行时动态环境变量方案
前端·react.js·docker·前端框架·vue·gitlab
Lee川1 小时前
Event Loop 面试通关:从原理到口述再到实战
前端·面试