在async中执行forEach,像瀑布一样

我试图通过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函数,该函数的作用与受throttleddebounced函数非常相似,除非任何未立即执行的调用都会随着速率限制时间段到期而排队并运行。

基本上,它会创建并行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();
}

以下是我将如何破解它(注意: arr是你的位置数组):

function populate(arr, callback, pos) {
    if(typeof pos == "undefined")
        pos=0;
    var path = '/maps/api/geocode/json?address='+encodeURIComponent(arr[pos].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);
    });
    pos++;

    if(pos<arr.length)
        setTimeout(function(){
            populate(arr,callback,pos);
        },110); //a little wiggle room since setTimeout isn't exact
    else
        callback();
}

你可以添加一个限速功能,但是,恕我直言,它引入了不必要的复杂性。 你真正想要做的就是每十分之一秒左右调用一次函数,直到完成你的列表为止。

这当然不像替代品那样具有可扩展性,但我是一个简单的粉丝。

链接地址: http://www.djcxy.com/p/75559.html

上一篇: Execute a forEach like a waterfall in async

下一篇: How to get the sum of two n