如何做到这一点,以便我可以在 javascript 中一次执行 10 个承诺,以防止对 api 调用的速率限制?

How to make it so that I can execute say 10 promises at a time in javascript to prevent rate limits on api calls?

我有 1000 条记录需要访问速率受限的 API 端点。我想让它在任何给定时间只有 5 个调用 URL,这样我就不会同时发出 1000 个请求。我怎样才能做到这一点?我有以下内容:

var Promise = require("bluebird");
var geocoder = Promise.promisifyAll(require('geocoder'));
var fs = require('fs');
var async = require('async');
var parse = require('csv-parse/lib/sync');
var inputFile = './myaddresses.txt'
var file = fs.readFileSync(inputFile, "utf8");

var records = parse(file, {columns: true});
var promises = [];
for(var i = 0; i < records.length; i++) {
    var placeName = records[i]['Place Name'];
            promises.push(geocoder.geocodeAsync(placeName));    
}

Promises.all(promises).then(function(result) {
  result.forEach(function(geocodeResponse) {
  console.log(geocodeResponse);
  })
}

要限制同时进行中的并发请求数,我建议使用 Bluebird 的 Promise.map(),它提供并发选项。它将为您执行以下所有操作:

  1. 迭代数组
  2. 将并发请求数限制为您将并发选项设置为
  3. 在最终结果数组中按顺序收集所有结果

以下是您将如何使用它:

const Promise = require('bluebird');

Promise.map(records, r => {
    let placeName = r['Place Name'];
    return geocoder.geocodeAsync(placeName));
}, {concurrency: 5}).then(results => {
    // all results here
}).catch(err => {
    // process error here
});

注意:速率限制通常与并发请求数并不严格相同。限制并发请求的数量将使您更有可能保持在速率限制之下,但不能保证。有特定的速率限制模块可以更直接地管理速率限制。


您可以使用 Bluebird 的 .delay() 为每个请求添加延迟。

const Promise = require('bluebird');

Promise.map(records, r => {
    let placeName = r['Place Name'];
    return geocoder.geocodeAsync(placeName)).delay(500);
}, {concurrency: 5}).then(results => {
    // all results here
}).catch(err => {
    // process error here
});

处理某些类型的速率限制的经典算法称为 leaky bucket algorithm


如果您的限制是 50 requests/sec,那么您只需确保您的并发数乘以您的延迟值永远不会超过 50/秒。

在没有库的情况下使用瀑布模式,并使用竞争条件在每次迭代时使用 reduce 来解决。并且可以通过在Array.from中指定数组的长度来限制调用次数。

var promise = Array.from({ length: 5 }).reduce(function (acc) {
  return acc.then(function (res) {
    return run().then(function (result) {
      res.push(result);
      return res;
    });
  });
}, Promise.resolve([]));


var guid = 0;
function run() {
  guid++;
  var id = guid;
  return new Promise(resolve => {
    // resolve in a random amount of time
    setTimeout(function () {
      console.log(id);
      resolve(id);
    }, (Math.random() * 1.5 | 0) * 1000);
  });
}