向API发出几个请求,每分钟只能处理20个请求

时间:2015-10-27 21:36:23

标签: javascript node.js

我有一个返回promise的方法,并且在内部该方法调用API,每分钟只能有20个请求。问题是我有大量的对象(大约300个),我想为每个对象调用API。

目前我有以下代码:

    const bigArray = [.....];

    Promise.all(bigArray.map(apiFetch)).then((data) => {
      ...
    });

但它没有处理时间限制。我希望我可以使用来自lodash的_.chunk和_.debounce之类的东西,但我无法绕过它。谁能帮助我?

2 个答案:

答案 0 :(得分:4)

如果你可以使用Bluebird promise库,它内置了一个并发功能,可以让你在一次飞行中管理一组异步操作,最多只有N个。

var Promise = require('bluebird');
const bigArray = [....];

Promise.map(bigArray, apiFetch, {concurrency: 20}).then(function(data) {
    // all done here
});

这个界面的好处在于它将保留20个飞行请求。它将启动20,然后每次完成,它将启动另一个。所以,这比发送20,等待所有人完成,发送20多个等等更有效...

这也以与bigArray完全相同的顺序提供结果,因此您可以确定哪个结果与哪个请求相关。

当然,您可以使用计数器使用通用承诺自行编码,但由于它已经在Bluebird库中构建,我认为我会推荐这种方式。

Async库也有类似的并发控制,虽然它显然不是基于承诺的。

这是一个只使用ES6承诺的手工编码版本,它保持结果顺序并始终保持20个飞行请求(直到没有剩余20个)以获得最大吞吐量:

function pMap(array, fn, limit) {
    return new Promise(function(resolve, reject) {
        var index = 0, cnt = 0, stop = false, results = new Array(array.length);

        function run() {
            while (!stop && index < array.length && cnt < limit) {
                (function(i) {
                    ++cnt;
                    ++index;
                    fn(array[i]).then(function(data) {
                        results[i] = data;
                        --cnt;
                        // see if we are done or should run more requests
                        if (cnt === 0 && index === array.length) {
                            resolve(results);
                        } else {
                            run();
                        }
                    }, function(err) {
                        // set stop flag so no more requests will be sent
                        stop = true;
                        --cnt;
                        reject(err);
                    });
                })(index);
            }
        }
        run();
    });
}   

pMap(bigArray, apiFetch, 20).then(function(data) {
    // all done here
}, function(err) {
    // error here
});

此处的演示演示:http://jsfiddle.net/jfriend00/v98735uu/

答案 1 :(得分:1)

您可以每分钟发送1个20个请求,或者每3秒发出1个请求(后者可能是API所有者首选)。

function rateLimitedRequests(array, chunkSize) {
  var delay = 3000 * chunkSize;
  var remaining = array.length;
  var promises = [];
  var addPromises = function(newPromises) {
    Array.prototype.push.apply(promises, newPromises);
    if (remaining -= newPromises.length == 0) {
      Promise.all(promises).then((data) => {
        ... // do your thing
      });
    }
  };
  (function request() {
    addPromises(array.splice(0, chunkSize).map(apiFetch));
    if (array.length) {
      setTimeout(request, delay);
    }
  })();
}

每3秒拨打一次电话:

rateLimitedRequests(bigArray, 1);

每分钟20次:

rateLimitedRequests(bigArray, 20);

如果您更喜欢使用_.chunk _.debounce 1 _.throttle

function rateLimitedRequests(array, chunkSize) {
  var delay = 3000 * chunkSize;
  var remaining = array.length;
  var promises = [];
  var addPromises = function(newPromises) {
    Array.prototype.push.apply(promises, newPromises);
    if (remaining -= newPromises.length == 0) {
      Promise.all(promises).then((data) => {
        ... // do your thing
      });
    }
  };
  var chunks = _.chunk(array, chunkSize);  
  var throttledFn = _.throttle(function() {
    addPromises(chunks.pop().map(apiFetch));
  }, delay, {leading: true});
  for (var i = 0; i < chunks.length; i++) {
    throttledFn();
  }
}

1 您可能需要_.throttle,因为它在延迟后执行每个函数调用,而_.debounce将多个调用分组到一个调用中。请参阅article

中链接的docs
  

去抖动:将其视为&#34;将多个事件分组在一个&#34;中。想象一下,你回家,进入电梯,门正在关闭......突然你的邻居出现在大厅里,试图跳上电梯。讲礼貌!并为他打开门:你正在解除电梯的离开。考虑到同样的情况可能会再次发生在第三个人身上,等等......可能会在几分钟内推迟出发。

     

节气门:将其视为阀门,它可以调节执行的流量。我们可以确定在特定时间内调用函数的最大次数。所以在电梯类比中......你很有礼貌地让人们进入10秒,但是一旦延迟过去,你必须去!

相关问题