开发Discord API交互库:限流队列如何返回REST请求结果?
解决Discord API限流队列请求结果返回问题
核心问题是当前入队列的请求仅返回队列长度,无法传递实际响应结果。要解决这个问题,需让每个入队请求关联一个Promise,待队列处理完成后通过resolve/reject传递结果或错误。
具体修改方案
1. 调整队列存储结构
不再仅存储请求配置,改为存储包含请求配置、Promise解析函数的对象:
// 修改ratelimit的queue类型,存储带resolve/reject的请求项 this.ratelimit = { queue: [] as Array<{ options: InternalRequest; resolve: (value: AxiosResponse | unknown) => void; reject: (reason?: any) => void; }>, timer: null as NodeJS.Timeout | null, time: null as number | null, };
2. 修改raw方法返回Promise
当请求需要入队时,创建Promise并将resolve/reject存入队列,返回该Promise给调用者:
public async raw(options: InternalRequest) { const globalRateLimit = Number(await this.REST.redisClient.incr("ratelimit:global")); if (globalRateLimit > 2) { await this.REST.redisClient.decr("ratelimit:global"); // 返回Promise,将resolve/reject绑定到队列项 return new Promise((resolve, reject) => { this.ratelimit.queue.push({ options, resolve, reject }); this.manageQueue(); }); } else { try { const result = await this.request(options); // 请求完成后递减限流计数 await this.REST.redisClient.decr("ratelimit:global"); return result; } catch (err) { await this.REST.redisClient.decr("ratelimit:global"); throw err; } } }
3. 调整processQueue方法传递结果
处理队列请求时,执行完成后调用对应resolve/reject传递结果:
private async processQueue() { if (this.ratelimit.queue.length === 0) { this.manageQueue(); return; } const globalRateLimit = Number(await this.REST.redisClient.incr("ratelimit:global")); if (globalRateLimit > 2) { await this.REST.redisClient.decr("ratelimit:global"); this.manageQueue(); return; } const queueItem = this.ratelimit.queue.shift(); if (!queueItem) return; try { const result = await this.request(queueItem.options); queueItem.resolve(result); await this.REST.redisClient.decr("ratelimit:global"); } catch (err) { queueItem.reject(err); await this.REST.redisClient.decr("ratelimit:global"); } // 继续处理下一个请求 if (this.ratelimit.queue.length > 0) { this.processQueue(); } else { this.ratelimit.timer = null; } }
4. 修复request方法的错误处理
当前catch仅打印错误,需抛出错误让上层Promise捕获:
private async request(options: InternalRequest) { console.log("REQUEST RUN!"); const request = this.formatRequest(options); const res = await axios(request.url, { method: options.requestMethod, headers: request.headers, }).catch((err) => { console.log("ERROR!"); throw err; // 抛出错误,让Promise捕获并传递给调用者 }); return this.parseResponse(res); // 返回解析后的数据 }
关键逻辑说明
- 每个入队请求绑定独立的Promise解析函数,调用
await this.client.rest.get(...)时,即使请求被入队,也会等待Promise resolve后拿到结果。 - 请求执行完成后必须递减Redis中的限流计数,避免计数累加导致后续请求无法正常执行。
- 队列处理前需再次检查限流状态,确保不会超出阈值再执行下一个请求。
内容的提问来源于stack exchange,提问作者Toby Chambers
相关产品推荐
相关产品推荐

