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

使用JS循环调用API触发429错误,求有效延迟请求方案

解决TomTom地理编码API返回429请求超限的问题

你现在遇到的问题是循环里批量发起的fetch请求几乎同时发送,直接触发了TomTom的请求频率限制。之前用setTimeout没效果,是因为循环会一次性创建所有定时器,到点后请求还是扎堆发,根本没错开时间。

下面给你两种可行的解决方案:

方案一:串行请求+固定间隔(最稳妥)

用async/await让每个请求完成后再发下一个,同时给每个请求加固定延迟,严格控制请求频率,符合API的限制要求。

修改后的完整代码

let koordinatlar = [];
const API_KEY = '你的API密钥'; // 替换成你实际的API Key

// 封装带延迟和重试的地理编码请求函数
async function getGeocode(address) {
  // 先等1秒(TomTom免费版通常限制1次/秒,可根据你的套餐调整)
  await new Promise(resolve => setTimeout(resolve, 1000));
  
  const encodedAddr = encodeURIComponent(address);
  const response = await fetch(`https://api.tomtom.com/search/2/geocode/${encodedAddr}.json?key=${API_KEY}`);

  // 遇到429就多等会儿再重试
  if (response.status === 429) {
    console.log('触发限流,2秒后重试');
    await new Promise(resolve => setTimeout(resolve, 2000));
    return getGeocode(address);
  }

  if (!response.ok) {
    throw new Error(`请求失败:${response.status}`);
  }

  return await response.json();
}

// 批量处理地址的主函数
async function processAllAddresses() {
  // 先把要处理的10条地址整理出来
  const targetAddresses = yesilListe[0].slice(0, 10).map(item => 
    `${item[5]} ${item[6]} ${item[7]} ${item[8]}`
  );

  for (let i = 0; i < targetAddresses.length; i++) {
    const currentAddr = targetAddresses[i];
    const currentItem = yesilListe[0][i];
    try {
      const geocodeData = await getGeocode(currentAddr);
      // 把结果存入数组
      koordinatlar.push([
        geocodeData.results[0].position.lon,
        geocodeData.results[0].position.lat,
        `${currentItem[2]} ${currentItem[3]}`,
        currentItem[4],
        currentAddr
      ]);
    } catch (err) {
      console.log(`处理第${i+1}条地址出错:`, err);
    }
  }

  // 所有请求完成后再打印结果,原代码里循环结束就打印会拿到空数组
  console.log(koordinatlar);
}

// 启动处理
processAllAddresses();

方案二:分批次并行请求(效率更高)

如果你的API套餐允许少量并发(比如5次/秒),可以分批次发送请求,每批次处理N个请求,批次之间加延迟,平衡效率和限流规则。

示例代码

let koordinatlar = [];
const API_KEY = '你的API密钥';
const BATCH_SIZE = 5; // 每批处理5个请求
const BATCH_DELAY = 1000; // 批次间隔1秒

async function getGeocode(address) {
  const encodedAddr = encodeURIComponent(address);
  const response = await fetch(`https://api.tomtom.com/search/2/geocode/${encodedAddr}.json?key=${API_KEY}`);

  if (response.status === 429) {
    console.log('触发限流,2秒后重试');
    await new Promise(resolve => setTimeout(resolve, 2000));
    return getGeocode(address);
  }

  if (!response.ok) {
    throw new Error(`请求失败:${response.status}`);
  }

  return await response.json();
}

async function processInBatches() {
  const targetItems = yesilListe[0].slice(0, 10);

  for (let i = 0; i < targetItems.length; i += BATCH_SIZE) {
    // 取出当前批次的条目
    const batchItems = targetItems.slice(i, i + BATCH_SIZE);
    // 并行处理当前批次的所有请求
    const batchPromises = batchItems.map(async item => {
      const address = `${item[5]} ${item[6]} ${item[7]} ${item[8]}`;
      try {
        const data = await getGeocode(address);
        return [
          data.results[0].position.lon,
          data.results[0].position.lat,
          `${item[2]} ${item[3]}`,
          item[4],
          address
        ];
      } catch (err) {
        console.log(`处理地址出错:`, err);
        return null;
      }
    });

    // 等待当前批次所有请求完成
    const batchResults = await Promise.all(batchPromises);
    // 过滤掉错误的结果,存入数组
    koordinatlar.push(...batchResults.filter(res => res !== null));

    // 如果不是最后一批,就等一会儿再发下一批
    if (i + BATCH_SIZE < targetItems.length) {
      await new Promise(resolve => setTimeout(resolve, BATCH_DELAY));
    }
  }

  console.log(koordinatlar);
}

processInBatches();

额外注意点

  • 原代码中console.log(koordinatlar)的位置不对,因为fetch是异步操作,循环结束时所有请求都还没完成,所以会打印空数组,上面的代码已经把打印移到所有请求完成后。
  • 一定要根据TomTom API的具体限制调整延迟时间,比如付费套餐可能允许更高的请求频率,延迟可以设短一点。
  • 增加了429错误的重试逻辑,避免偶尔的限流导致请求失败。

内容的提问来源于stack exchange,提问作者Fuber

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 02:40:18