使用Promises与Axios调用TomTom逆地理编码API仍返回429错误的技术求助
解决TomTom Reverse Geocode API 429错误及批量请求问题
我来帮你一步步排查和解决这个问题,先从你原代码里的核心问题说起,再给出优化后的方案:
一、原函数里的关键错误
你的代码之所以还会出现429,甚至重试逻辑失效,主要有几个问题:
- 重试参数传递错误:catch块里递归调用
getTopographyData(counter),但这个函数需要的是经纬度参数latLang,你传成了重试次数counter,导致后续重试的请求完全无效。 - 异步流程未串联:虽然函数声明了
async,但内部用.then()链式调用却没有返回整个Promise链,外部批量调用时会同时发起所有请求,根本没做到间隔5秒——你只在重试时加了等待,但初始的批量请求是并行的,这才是触发429的核心原因。 - setTimeout的异步处理不清晰:重试时用Promise包裹setTimeout的写法没问题,但递归时没有正确传递原有的经纬度数据,导致重试逻辑完全偏离目标。
二、修复后的单请求函数
先把单个请求的逻辑修正,确保重试正确,且返回Promise方便批量控制:
async function getTopographyData(latLang) { const retryTimes = 5; let counter = 0; // 封装内部异步函数,方便重试调用 const fetchData = async () => { try { // 先获取地形数据 const topographyResults = await Topography.getTopography(latLang, options); let newObj = { topography: topographyResults, latlng: latLang }; // 调用TomTom逆地理编码API const { lat, lng } = newObj.latlng; const tomTomResponse = await axios.get( `https://api.tomtom.com/search/2/reverseGeocode/crossStreet/${lat},${lng}.json?limit=1&spatialKeys=false&radius=10000&allowFreeformNewLine=false&view=Unified&key=${process.env.TOM_TOM_API}` ); const { addresses } = tomTomResponse.data; if (!addresses || addresses.length === 0) { throw new Error("TomTom未返回地址数据"); } const { address, position } = addresses[0]; const [newLat, newLng] = position.split(",").map(Number); newObj = { ...newObj, latlng: { lat: newLat, lng: newLng }, address }; dispatch({ type: "setTopographyData", payload: newObj }); return newObj; } catch (error) { if ((error.response?.status === 403 || error.response?.status === 429) && counter < retryTimes) { counter++; console.log(`正在重试请求 (${counter}/${retryTimes}):${JSON.stringify(latLang)}`); // 优先遵循TomTom返回的Retry-After头,没有则用固定5秒 const waitTime = error.response?.headers['retry-after'] ? parseInt(error.response.headers['retry-after'], 10) * 1000 : 5000; await new Promise(resolve => setTimeout(resolve, waitTime)); return fetchData(); // 递归调用,保持原经纬度参数 } else { console.error(`获取数据失败 ${JSON.stringify(latLang)}:`, error); throw error; // 抛出错误让外部批量处理逻辑决定是否继续 } } }; return fetchData(); }
三、批量处理72个经纬度的间隔控制
单个请求逻辑修正后,必须串行发起请求,确保每个请求间隔足够时间,避免触发速率限制:
async function processBatch(latLngList) { for (const latLng of latLngList) { try { await getTopographyData(latLng); // 成功后等待5秒再发起下一个请求 await new Promise(resolve => setTimeout(resolve, 5000)); } catch (error) { // 单个请求失败时,可选择跳过并继续处理下一个 console.log(`跳过经纬度 ${JSON.stringify(latLng)}`); // 即使失败也等待5秒,避免触发更多限制 await new Promise(resolve => setTimeout(resolve, 5000)); } } } // 调用示例:把你的72个经纬度数组传入即可 // processBatch(yourLatLngArray);
四、关于第45个请求失败但浏览器直接访问正常的问题
这个情况大概率是连续请求的累积速率限制,TomTom的API限制可能不只是单请求间隔,还有每分钟/小时的总请求数上限。你可以尝试:
- 把请求间隔从5秒延长到6-10秒,看是否能完成全部72个请求
- 登录TomTom开发者后台查看API调用统计,确认是否触发了其他类型的限制
- 利用前面代码里的
Retry-After头逻辑,动态遵循TomTom给出的等待建议,比固定5秒更可靠
内容的提问来源于stack exchange,提问作者Antonio Pavicevac-Ortiz
相关产品推荐
相关产品推荐

