在前端开发中,批量处理异步请求是常见需求,比如批量上传图片、爬取数据、导入大批量信息等。但如果不加控制,可能会遇到接口限流、浏览器卡顿、甚至服务端拒绝请求等问题。今天带你用最简单的方式,掌握两种常用的 Promise 并发控制技巧,并结合实际开发场景,分析常见坑点和解决办法!
场景一:批量执行(分批并发)
应用场景举例:
- 批量上传图片时,后端只允许每次最多5个请求,否则会返回"请求过多"错误。
- 需要分批导入数据,保证每批都能被后端稳定处理。
常见问题:
- 一次性全部发起请求,容易被限流或接口报错。
- 没有分批,用户体验差,失败率高。
解决思路:将任务分成一批一批,每批最多 N 个,上一批全部完成后再执行下一批。
实现代码如下:
js
// 并发分批执行,每批limit个,上一批全部完成后再执行下一批
function batchRequest(tasks, limit) {
const results = new Array(tasks.length);
let index = 0;
const runBatch = () => {
if (index >= tasks.length) return Promise.resolve();
const batch = tasks.slice(index, index + limit).map((task, i) => {
const realIndex = index + i;
return Promise.resolve(task())
.then(value => ({ status: 'fulfilled', value }))
.catch(reason => ({ status: 'rejected', reason }))
.then(result => {
results[realIndex] = result;
});
});
index += limit;
return Promise.all(batch).then(() => {
if (index < tasks.length) {
console.log('本批次执行完毕,将要执行下一个批次');
}
return runBatch();
});
}
return runBatch().then(() => results);
}
// 模拟异步任务
const mockTask = (id, duration) => {
return () =>
new Promise(resolve => {
console.log(`[排队] 任务${id} 进入队列,需要 ${duration}ms`);
setTimeout(() => resolve(`任务${id}结果`), duration);
});
};
// 创建测试任务
const tasks = [
mockTask(1, 2000),
mockTask(2, 1500),
mockTask(3, 800),
mockTask(4, 1200),
mockTask(5, 3000),
mockTask(6, 500),
mockTask(7, 1800),
mockTask(8, 600),
mockTask(9, 900),
mockTask(10, 700),
];
// 用法示例
batchRequest(tasks, 3).then(results => {
console.log('====== 所有任务完成 ======');
console.log('最终结果:', results);
});
运行结果:

场景二:最大并发数控制(随时补位)
应用场景举例:
- 爬虫抓取数据时,既要快又不能让服务器崩溃
- 前端批量请求接口,既想高效又要防止被限流
常见问题:
- 没有限流,接口容易被封
- 并发数太低,效率又太低
解决思路:始终保持 N 个任务在执行,有空位就立刻补新任务,直到全部完成。
实现代码如下:
js
// 最大并发数控制,随时补位
function controlRequest(tasks, limit) {
return new Promise(resolve => {
const results = new Array(tasks.length);
let currentIndex = 0;
let completeCount = 0;
let currentRunCount = 0;
const runTask = () => {
while (currentIndex < tasks.length && currentRunCount < limit) {
const taskIndex = currentIndex++;
currentRunCount++;
const currentTask = tasks[taskIndex];
console.log(`[启动] 任务${taskIndex + 1} 开始 (并发数:${currentRunCount})`);
Promise.resolve(currentTask())
.then(value => { results[taskIndex] = { status: 'fulfilled', value }; })
.catch(reason => { results[taskIndex] = { status: 'rejected', reason }; })
.finally(() => {
completeCount++;
currentRunCount--;
if (completeCount === tasks.length) {
resolve(results);
} else {
runTask();
}
});
}
}
runTask();
});
}
// 模拟异步任务
const mockTask = (id, duration) => {
return () =>
new Promise(resolve => {
console.log(`[排队] 任务${id} 进入队列,需要 ${duration}ms`);
setTimeout(() => resolve(`任务${id}结果`), duration);
});
};
// 创建测试任务
const tasks = [
mockTask(1, 2000),
mockTask(2, 1500),
mockTask(3, 800),
mockTask(4, 1200),
mockTask(5, 3000),
mockTask(6, 500),
mockTask(7, 1800),
mockTask(8, 600),
mockTask(9, 900),
mockTask(10, 700),
];
// 用法示例
controlRequest(tasks, 3).then(results => {
console.log('====== 所有任务完成 ======');
console.log('最终结果:', results);
});
运行结果:

欢迎在评论区留言交流,如果觉得有用,记得点赞+关注