批量API请求分批处理及批次间超时设置问题求助
解决大规模API请求的超时与403问题
问题核心
一次性用Promise.all发起1000次API请求时,触发了API的限流机制,导致超时和随机403错误。你尝试的两种方案都没生效,核心原因是提前执行了所有请求,并发控制逻辑根本没起到作用。
失效方案分析
1. Bluebird方案失效原因
你先通过requests.map把所有请求转换成Promise并立即执行了,之后再用BlueBirdPromise.map去处理这些已经在运行的Promise——这时候请求早就发出去了,concurrency配置完全没意义,只是在等待结果而已。
2. 原生批量方案失效原因
同样的问题:requests.map(async request => await service.retryFailedRequest(request))会立即触发所有1000次请求,生成的是1000个已处于pending状态的Promise,不是待执行的函数。后续的分批和延迟逻辑对已经发起的请求没有任何影响。
正确解决方案
方案1:修正Bluebird并发控制
直接把请求数组传给BlueBirdPromise.map,并传入请求执行逻辑,让Bluebird控制请求的执行时机:
const retry = async (requests: API.CarFailedRequest[]) => { setIsLoading(true); const results = await BlueBirdPromise.map( requests, async (request) => { try { await service.retryFailedRequest(request); return { status: true, request }; } catch (e) { return { status: false, request }; } }, { concurrency: 10 } // 控制同时发起10个请求 ); setIsLoading(false); // 可根据results统计成功/失败请求 };
方案2:原生实现并发控制
无需第三方库,核心是将请求逻辑包装成函数,延迟执行,再通过代码控制并发数或分批执行。
方式A:分批执行(每批N个,批次间加延迟)
适合API对请求频率限制严格的场景:
// 批量处理函数:接收请求执行函数数组、批次延迟、每批数量 const processRequestsWithDelay = async ( requestFns: (() => Promise<any>)[], delaySeconds: number, batchSize: number ) => { const results = []; for (let i = 0; i < requestFns.length; i += batchSize) { const batch = requestFns.slice(i, i + batchSize); // 执行当前批次所有请求 const batchResults = await Promise.all(batch.map(fn => fn())); results.push(...batchResults); // 非最后一批则添加延迟 if (i + batchSize < requestFns.length) { await new Promise(resolve => setTimeout(resolve, delaySeconds * 1000)); } } return results; }; const retry = async (requests: API.CarFailedRequest[]) => { setIsLoading(true); // 包装请求为函数,不立即执行 const requestFns = requests.map(request => () => service.retryFailedRequest(request) ); // 每批5个请求,批次间隔5秒 const results = await processRequestsWithDelay(requestFns, 5, 5); // 处理结果,标记成功/失败 const processedResults = results.map((_, idx) => ({ status: !!_, request: requests[idx] })); setIsLoading(false); };
方式B:严格控制并发数(始终保持N个请求运行)
适合需要更高执行效率的场景,无需等整批完成再启动下一批:
// 创建限流器:控制同时运行的请求数量 const createRateLimiter = (concurrency: number) => { const queue: (() => Promise<any>)[] = []; let activeCount = 0; const execute = async () => { if (activeCount >= concurrency || queue.length === 0) return; activeCount++; const fn = queue.shift(); try { await fn!(); } catch (e) { console.error('请求失败:', e); } finally { activeCount--; execute(); } }; return { // 添加待执行的请求函数 add: (fn: () => Promise<any>) => { queue.push(fn); execute(); }, // 等待所有请求完成 wait: () => new Promise(resolve => { const checkCompletion = () => { if (activeCount === 0 && queue.length === 0) resolve(null); else setTimeout(checkCompletion, 100); }; checkCompletion(); }) }; }; const retry = async (requests: API.CarFailedRequest[]) => { setIsLoading(true); const limiter = createRateLimiter(10); // 同时运行10个请求 const results: { status: boolean; request: API.CarFailedRequest }[] = []; requests.forEach(request => { limiter.add(async () => { try { await service.retryFailedRequest(request); results.push({ status: true, request }); } catch (e) { results.push({ status: false, request }); } }); }); await limiter.wait(); // 等待所有请求执行完毕 setIsLoading(false); };
关键注意事项
- 所有并发控制方案的核心是不要提前执行请求,必须把请求逻辑包装成函数,在需要的时候才调用执行。
- 根据API的限流规则调整
concurrency(并发数)和延迟时间,如果仍出现403,可进一步降低并发数或增加延迟。 - 建议在请求失败时添加重试逻辑(比如最多重试3次),提升成功率。
内容的提问来源于stack exchange,提问作者mouchin777
相关产品推荐
相关产品推荐

