Fetch本身不提供并发控制,需借助Promise.allSettled分批执行、Semaphore信号量或Promise.race超时/竞速等机制实现;核心是限制同时请求数量,避免压垮服务端或触发限流。

Fetch 本身不提供并发控制机制,需要借助 Promise、async/await 和工具函数(如 Promise.allSettled、Promise.race 或自定义限流)来实现。核心思路是限制同时发起的请求数量,避免瞬间大量请求压垮服务端或触发浏览器/服务端限流。
用 Promise.allSettled + 分批执行控制并发数
适用于需全部执行完、且要保留每个请求结果(包括失败)的场景。把请求数组切分成每组 N 个,依次执行批次,等前一批全完成再发下一批。
- 先封装一个 limitConcurrency 工具函数:接收请求函数数组和最大并发数,返回所有响应的 Promise 数组
- 每次从待处理队列中取出最多 max 个请求,调用 Promise.allSettled 执行
- 等这批完成,再取下一批,直到队列为空
示例:
async function limitConcurrency(requests, max = 3) {
const results = [];
for (let i = 0; i < requests.length; i += max) {
const batch = requests.slice(i, i + max);
const batchResults = await Promise.allSettled(batch);
results.push(...batchResults);
}
return results;
}
// 使用
const urls = ['/api/1', '/api/2', '/api/3', '/api/4', '/api/5'];
const fetchPromises = urls.map(url => fetch(url).then(r => r.json()));
const results = await limitConcurrency(fetchPromises, 2); // 最多 2 个并发
用 async/await + 信号量(Semaphore)精细控制
适合对并发粒度要求高、或需复用同一限流器的场景(比如多个地方共用 5 并发)。通过计数器 + 队列协调请求排队与释放。
立即学习“Java免费学习笔记(深入)”;
- 创建一个 Semaphore 类:维护当前可用许可数、等待队列
- 每次请求前 acquire(),获取许可后才发请求;完成后 release() 归还许可
- 无可用许可时,自动 await 挂起,等前面请求完成腾出位置
示例:
class Semaphore {
constructor(max) {
this.max = max;
this.available = max;
this.queue = [];
}
acquire() {
return new Promise(resolve => {
if (this.available > 0) {
this.available--;
resolve();
} else {
this.queue.push(resolve);
}
});
}
release() {
if (this.queue.length > 0) {
const resolve = this.queue.shift();
resolve();
} else {
this.available++;
}
}
}
const sem = new Semaphore(3);
const fetchWithLimit = async (url) => {
await sem.acquire();
try {
const res = await fetch(url);
return res.json();
} finally {
sem.release();
}
};
用 Promise.race 实现超时或快速失败控制
并发控制不仅指数量,也包括“及时止损”。可在单个请求上加超时,或在一组请求中只取最快响应(如 DNS 多源探测)。
- 单请求超时:Promise.race([fetch(), timeoutPromise]),任一先完成即返回
- 竞速取最快:Promise.race(fetchPromises),只要有一个成功就结束,其余自动忽略(注意 abortController 可取消未完成请求)
- 组合使用:每个 fetch 都带超时,再用 allSettled 分批,形成双重保护
注意事项与避坑点
- AbortController 要配合使用:分批或限流时,若用户取消操作,应主动 abort 当前批次中未完成的请求
- 错误不能被 Promise.all 吞掉:优先用 allSettled,避免一个失败导致整批中断
- 浏览器有默认并发限制(Chrome 对同一域名约 6 个),限流值设太高无意义,一般 3–6 较合理
- 服务端可能有更严格的限流策略(如 token bucket),前端限流只是辅助,不能替代后端防护


















