如何限制并发请求的数量避免浏览器卡死?
我有个页面要批量上传几十个文件,直接用 Promise.all 发起请求,结果浏览器直接卡住了,甚至报错“Too many connections”。
试过自己写了个队列控制,但逻辑有点乱,比如下面这样:
async function uploadWithLimit(files, limit = 3) {
const results = [];
for (let i = 0; i < files.length; i += limit) {
const chunk = files.slice(i, i + limit);
const promises = chunk.map(file => fetch('/upload', { method: 'POST', body: file }));
results.push(...await Promise.all(promises));
}
return results;
}
但这样还是会在每批内并发太多,而且不能动态调度空闲的“槽位”。有没有更优雅的并发控制方案?
Promise.race配合Set来实现一个真正的并发池。核心思路是:始终维护一个正在执行的 Promise 集合,一旦有请求完成,立刻补上下一个,这样永远保持最大并发数,不会出现空窗期。
然后你的上传函数就变得很干净了:
这里有几个关键点需要说明一下。
用
Set存储正在执行的 Promise,配合finally自动删除已完成的任务,这样Promise.race每次都能正确感知到"有空位了"。任务要包装成函数
() => fetch(...)而不是直接传 Promise,否则循环的时候所有请求就已经发出去了,根本控制不住。结果用索引赋值
results[index] = result,这样即使任务完成顺序乱掉,最终结果顺序还是跟输入保持一致。这种写法比你自己写的 for 循环分批要优雅很多,而且能最大化利用并发槽位,不会出现"等最慢的那个"的情况。