如何避免请求队列中频繁API调用被限流?
我正在做一个实时数据同步功能,需要连续发送大量POST请求到API,但总被服务器限流返回429。我尝试用队列加setTimeout控制频率,但实际测试发现请求还是挤在一起发送了,代码哪里有问题?
let queue = [];
const MAX_CONCURRENT = 3;
let processing = false;
function enqueueRequest(data) {
queue.push(data);
if (!processing) {
processing = true;
processQueue();
}
}
async function processQueue() {
while (queue.length > 0 && MAX_CONCURRENT > 0) {
const item = queue.shift();
await fetch('/api/submit', {method: 'POST', body: item})
.then(() => setTimeout(processQueue, 1000)) // 每次请求间隔1秒
.catch(err => queue.unshift(item)); // 失败重新入队
MAX_CONCURRENT--;
}
processing = false;
}
按理说设置了1秒间隔和并发限制,但监控发现请求还是在0.5秒内发出了5个,服务器返回的X-RateLimit-Remaining直接归零。是不是队列处理逻辑有竞态条件?或者Promise链没串起来?
setTimeout(processQueue, 1000)这种写法根本不会阻塞执行,只是异步调度了一下,导致多个processQueue实例并行跑起来了,完全破坏了并发控制。最致命的是你在
await fetch().then(...)里又调用了setTimeout(processQueue),但await只等fetch,不等那个setTimeout后续。结果就是第一个请求发完,还没过一秒,await就结束了,继续下一轮while循环,瞬间把后面几个全发出去了。而且你还写了MAX_CONCURRENT--,但它是个常量,每次减都没用,下一次调用又重新从3开始?这变量放的位置也不对。还有,并发数控制不能靠 while 循环 + await 单个请求,得用 Promise.all 控制最大并发,同时每个请求之间要有真实延迟。
直接给你一个能跑的版本:
关键点:
-
MAX_CONCURRENT控制每轮并发数,不是拿全局变量去减- 批次之间用
await new Promise(setTimeout)真实等待- 失败重试独立封装,别往队列头 unshift,容易死循环
- 注意处理服务器返回的
Retry-After头,这是标准做法你现在的问题本质是 JS 里面异步流程控制没理清,
await和setTimeout混用很容易踩坑。记住:await只等 Promise,不等 callback 回调。