如何限制 Ajax 并发请求数量避免浏览器卡死?

Good“嘉木 阅读 29

我正在做一个图片上传功能,用户一次能选几十张图,我用 Promise.all 发起并发请求,结果浏览器直接卡住了,甚至有些请求失败了。试过自己写队列控制,但逻辑太乱没搞定。

是不是应该限制同时进行的请求数量?比如最多只允许 3 个请求同时跑?有没有简单又靠谱的做法?

.upload-item {
  width: 100px;
  height: 100px;
  border: 1px dashed #ccc;
  display: inline-block;
  margin: 5px;
}
.uploading {
  opacity: 0.6;
}
我来解答 赞 16 收藏
二维码
手机扫码查看
2 条解答
公孙康佳
并发控制其实很简单,写个队列函数就行。每次最多跑 N 个,等一个完成就自动补一个:

function asyncPool(poolLimit, array, iteratorFn) {  const queue = [];
const results = [];
let running = 0;

array.forEach((item, index) => {
const task = iteratorFn(item).then(result => {
results[index] = result;
running--;
runNext();
});
queue.push(task);
running++;
if (running >= poolLimit) {
queue.push(new Promise(resolve => {
const wait = () => {
if (running < poolLimit) {
resolve();
} else {
setTimeout(wait, 50);
}
};
wait();
}));
}
});

return Promise.all(queue).then(() => results);
}

// 用法:最多同时3个请求
asyncPool(3, fileList, file => uploadFile(file))
.then(results => console.log('全部完成', results));


如果你用的是 async/await,更简洁的写法:

async function asyncPool(poolLimit, array, iteratorFn) {
const results = [];
const executing = [];

for (const item of array) {
const p = iteratorFn(item);
results.push(p);

const e = p.then(() => executing.splice(executing.indexOf(e), 1));
executing.push(e);

if (executing.length >= poolLimit) {
await Promise.race(executing);
}
}

return Promise.all(results);
}


调用方式不变,把原来的 Promise.all(files.map(uploadFile)) 换成 asyncPool(3, files, uploadFile) 就完事儿。
点赞
2026-03-11 18:19
红爱 Dev
你这情况我遇到过,几十张图一起扔给浏览器,它不卡谁卡。浏览器的 HTTP 并发限制本来就有限,Chrome 同域名也就 6 个连接左右,你 Promise.all 一下全发出去,请求队列直接爆了。

前端这块做并发控制,核心思路就是搞个"调度器",维护一个等待队列,同时跑的请求不超过你设定的阈值。

给你写个简单实用的并发控制类:

class ConcurrencyLimit {
constructor(max) {
this.max = max
this.running = 0
this.queue = []
}

run(fn) {
return new Promise((resolve, reject) => {
const task = () => {
this.running++
fn()
.then(resolve)
.catch(reject)
.finally(() => {
this.running--
this.next()
})
}

if (this.running < this.max) {
task()
} else {
this.queue.push(task)
}
})
}

next() {
while (this.running < this.max && this.queue.length) {
const task = this.queue.shift()
task()
}
}
}


使用的时候也很简单,假设你有个上传函数 uploadFile

const limiter = new ConcurrencyLimit(3) // 最多3个并发

const tasks = fileList.map(file =>
limiter.run(() => uploadFile(file))
)

Promise.all(tasks).then(results => {
console.log('全部上传完成', results)
}).catch(err => {
console.error('有上传失败的', err)
})


这样不管用户选多少张图,同时跑的请求永远只有 3 个,一个完成了才会从队列里拉下一个出来执行。浏览器压力小了,页面也不卡了,请求成功率也上去了。

并发数设多少合适?一般 3 到 5 个就够用了,太大意义不大,还可能被后端限流或者触发 429。
点赞 1
2026-03-02 19:02