You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 14:23:11