如何在JavaScript获取题库时处理API速率限制?
解决方案:结合请求节流、缓存与重试机制
针对Open Trivia Database API的5秒速率限制,我们可以通过三种核心策略避免触发限制,同时控制错误日志输出:
1. 请求节流与队列管理
- 维护请求队列,确保任意两次API调用间隔不小于5秒
- 新请求发起时,若距离上次请求不足5秒则加入队列等待
- 队列请求按顺序执行,每次间隔严格保持5秒,从根源上规避速率限制
2. 结果缓存
- 对相同分类+难度的请求结果做缓存,有效期设为5分钟(可按需调整)
- 后续相同参数请求直接返回缓存数据,减少API调用次数同时提升响应速度
3. 速率限制重试机制
- 识别API返回的
response_code=4(速率限制触发),此时不直接抛出错误,等待5秒后自动重试 - 仅在非速率限制的错误场景输出控制台日志,避免错误信息刷屏
修改后的完整代码
// 闭包维护请求状态与缓存,避免污染全局作用域 const requestQueue: (() => Promise<Question[]>)[] = []; let lastRequestTime = 0; const RATE_LIMIT_INTERVAL = 5000; // 5秒间隔 const CACHE_TTL = 5 * 60 * 1000; // 5分钟缓存有效期 const questionCache = new Map<string, { data: Question[], timestamp: number }>(); export const fetchQuizQuestions = async ( category: string, difficulty: string ): Promise<Question[]> => { const cacheKey = `${category}-${difficulty}`; // 优先检查缓存 const cachedData = questionCache.get(cacheKey); if (cachedData && Date.now() - cachedData.timestamp < CACHE_TTL) { return cachedData.data; } // 封装核心请求逻辑 const executeRequest = async (): Promise<Question[]> => { try { const response = await fetch( `https://opentdb.com/api.php?amount=5&category=${category}&difficulty=${difficulty}&type=multiple` ); const data = await response.json(); if (data.response_code === 0) { // 更新缓存 questionCache.set(cacheKey, { data: data.results as Question[], timestamp: Date.now() }); return data.results as Question[]; } else if (data.response_code === 4) { // 触发速率限制,等待后重试 await new Promise(resolve => setTimeout(resolve, RATE_LIMIT_INTERVAL)); return executeRequest(); } else { throw new Error(`Failed to fetch questions: response code ${data.response_code}`); } } catch (error) { // 仅非速率限制错误输出日志 if (!(error instanceof Error && error.message.includes('response code 4'))) { console.error('Error fetching quiz questions:', error); } throw error; } }; // 处理请求节流逻辑 const now = Date.now(); if (now - lastRequestTime < RATE_LIMIT_INTERVAL) { return new Promise((resolve, reject) => { requestQueue.push(async () => { try { resolve(await executeRequest()); } catch (err) { reject(err); } }); // 启动队列处理定时器 if (requestQueue.length === 1) { const waitTime = RATE_LIMIT_INTERVAL - (now - lastRequestTime); setTimeout(async () => { lastRequestTime = Date.now(); let nextRequest = requestQueue.shift(); if (nextRequest) await nextRequest(); // 依次处理剩余队列请求 while (requestQueue.length > 0) { await new Promise(resolve => setTimeout(resolve, RATE_LIMIT_INTERVAL)); lastRequestTime = Date.now(); nextRequest = requestQueue.shift(); if (nextRequest) await nextRequest(); } }, waitTime); } }); } else { lastRequestTime = now; return executeRequest(); } };
内容的提问来源于stack exchange,提问作者SoyMario
相关产品推荐
相关产品推荐

