浏览器对同一域名的fetch并发数有硬限制(如Chrome为6个),不可配置或绕过;需手动控制主动发起的并发请求数,避免内存暴涨、页面卡顿及触发服务端429限流。

浏览器本身对同一域名的 fetch 并发数有硬限制(Chrome 是 6 个),但这个限制不可配置、不可绕过,也和你的业务逻辑无关。真正需要你手动控制的,是「你主动发起的并发请求数」——比如一次上传 50 个文件,或批量拉取 100 条商品数据。不加控制,轻则内存暴涨、页面卡死,重则触发服务端限流甚至 429。
用 p-limit 最快落地,但要注意它不处理中断和错误重试
p-limit 是最轻量、最常用的选择,底层就是队列 + 计数器,源码不到 30 行。它不侵入你的请求函数,只包装执行时机:
- 安装:
npm install p-limit - 使用:
const limit = pLimit(3)返回一个函数,调用它时自动排队 - 每个被
limit()包裹的函数必须返回 Promise;如果里面用了fetch,记得catch错误,否则失败会静默吞掉 - 它不提供
abort()能力,想取消某次请求得自己在 fetch 里传{ signal },并确保上层能透出 AbortController 实例
手写队列控制器:必须管理 running 和 queue 两个状态
核心就两件事:当前有几个请求在跑(running),还有几个等着(queue)。每次请求结束,立刻从队列取下一个,而不是等全部 batch 完再开下一波——后者响应延迟高、资源利用率低:
-
running初始为 0,每次fetch开始前++,.finally(() => running--) -
queue存的是带resolve/reject的任务对象,不是裸 URL;否则你无法把结果正确回传给调用方 - 不要在
while循环里直接await fetch—— 这会阻塞整个队列调度,应该用.then()链式推进 - 如果请求函数本身可能 throw 同步错误(比如参数校验失败),需在外层
try/catch包一层,否则队列会卡住
大文件上传场景下,并发控制必须配合 AbortController 和引用释放
限制并发数只是第一步。上传几十个 100MB 文件时,若不做清理,FormData.append('file', file) 会让所有 File 对象长期驻留内存,Chrome DevTools 的 Memory 面板里能看到 JS Heap 持续上涨:
立即学习“前端免费学习笔记(深入)”;
- 每个
fetch必须传{ signal: controller.signal },且 controller 实例要和单个请求生命周期绑定 - 上传中途取消时,
controller.abort()只能终止网络传输,服务端可能已收到部分分片,后端必须做幂等处理 - 请求结束后,显式将
file、formData置为null,尤其在循环或闭包中,避免意外持有引用 - 别用
URL.createObjectURL(file)做预览后忘记revokeObjectURL,这也是常见内存泄漏点
别忽略浏览器原生并发上限与域名分流策略
即使你代码里只允许 3 个 fetch 并发,如果所有请求都打向 https://api.example.com,第 7 个请求仍会被浏览器挂起,直到前面有空位——这个等待不经过你的队列逻辑,你完全感知不到:
- 检查 Network 面板里请求状态是否长时间卡在
pending,大概率是撞上浏览器域名级并发限制了 - 真实项目中可考虑域名分流(如
api1.example.com/api2.example.com),但需服务端支持跨域或泛域名证书 - CDN 场景下注意:某些边缘节点对并发连接数也有额外限制,和浏览器行为叠加后更难排查
真正麻烦的从来不是“怎么写个限并发函数”,而是当上传卡住、内存飙升、请求莫名 pending 时,你能否快速定位到底是队列逻辑错了、引用没释放、还是撞上了浏览器那层看不见的墙。



















