我正在尝试通过Node.js脚本从Google API的地址列表中检索经度和纬度.呼叫本身工作正常,但因为我有大约100个地址要提交.我在一个数组上使用async.forEach,但是调用太快了,我得到错误“你已经超过了这个API的速率限制”.
我发现每24小时呼叫次数限制为2500次,每秒最多10次.虽然我每天可以使用2500个,但是我的通话速度太快了.
我现在必须编写一个函数来延迟调用,不要达到极限.以下是我的代码示例:
async.forEach(final_json,function(item,callback) { var path = '/maps/api/geocode/json?address='+encodeURIComponent(item.main_address)+'&sensor=false'; console.log(path); var options = { host: 'maps.googleapis.com',port: 80,path: path,method: 'GET',headers: { 'Content-Type': 'application/json' } } // a function I have who makes the http GET rest.getJSON(options,function(statusCode,res) { console.log(res); callback(); }); },function() { // do something once all the calls have been made });
你会如何实现这一目标?我尝试将我的rest.getJSON放在一个100ms的setTimeout中但是forEach迭代遍历所有的行,所以它几乎同时启动所有的setTimeout,因此它不会改变任何东西……
async.waterfall看起来就像它可以做的那样,但问题是我不确切知道我将拥有多少行,因此我无法对所有函数调用进行硬编码.说实话,它会让我的代码变得非常丑陋
解决方法
这个想法是你可以创建一个rateLimited函数,它的作用就像一个限制或去抖动函数,除了任何不立即执行的调用在速率限制时间到期后按顺序排队并运行.
基本上,它创建并行1秒间隔,通过定时器重新调度自我管理,但只允许最多perSecondLimit间隔.
function rateLimit(perSecondLimit,fn) { var callsInLastSecond = 0; var queue = []; return function limited() { if(callsInLastSecond >= perSecondLimit) { queue.push([this,arguments]); return; } callsInLastSecond++; setTimeout(function() { callsInLastSecond--; var parms; if(parms = queue.shift()) { limited.apply(parms[0],parms[1]); } },1010); fn.apply(this,arguments); }; }
用法:
function thisFunctionWillBeCalledTooFast() {} var limitedVersion = rateLimit(10,thisFunctionWillBeCalledTooFast); // 10 calls will be launched immediately,then as the timer expires // for each of those calls a new call will be launched in it's place. for(var i = 0; i < 100; i++) { limitedVersion(); }