for循环中的速率限制请求承诺

Rate limiting request-promise in for loop

我正在尝试使用 promises 来限制我向外部 API 发送 GET 请求的速率,但我很难让它正常工作。在我的场景中,我正在使用 'request-promise' 模块,我需要从 API 发送针对 175 个项目的 GET 请求(每个项目 ID 一个请求)。 API 的速率限制为每 10 秒 40 个请求,因此我的限制需要为每个请求 250 毫秒。我正在尝试为每个项目 ID 发送内部循环请求,例如:

files.forEach(function (file, i) {
    console.log("The item ID is " + file.match(re)[1]);
    client.send(new APIClient.requests.getItem(file.match(re)[1]))
    .then((item) => {
      ...
    })
    .catch((error) => {
      console.error(error);
      // Use fallback
    });
    ...

这是我的 API 客户端 return 请求承诺 (rp) 的片段,超时为 250 毫秒且无回调:

const rp = require('request-promise');
const rp_errors = require('request-promise/errors');
...
send(request, callback) {
...
    return rp(options)
               .then(this._parseResponse)
               .then((response)=> {
                  return new Promise( (resolve) => setTimeout(function(){
                    if (callback) { return callback(null, response); }
                    return resolve(response);
                  }, 250));
                })
                .catch(rp_errors.StatusCodeError,((error) => {
                    throw new errors.ResponseError(request, error.statusCode, error.message);
                  }
                ))
                .catch(rp_errors.RequestError,((error) => {
                    if(error.cause.code === 'ETIMEDOUT' || error.cause.code === 'ESOCKETTIMEDOUT')
                      throw new errors.TimeoutError(request, error);
                    throw error;
                  }
                ))
                .catch((error) => {
                  if (callback) {return callback(error)};
                  throw error;
                });
}

异步不起作用,return这是“超过 429 个请求限制”的堆栈跟踪

{ ResponseError: 429 - {"status_code":25,"status_message":"Your request count (175) is over the allowed limit of 40."}
[0]     at rp.then.then.catch (/mnt/c/Users/ridhwaan/Source/homehost/lib/api-client.js:52:19)
[0]     at tryCatcher (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/util.js:16:23)
[0]     at /mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/catch_filter.js:17:41
[0]     at tryCatcher (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/util.js:16:23)
[0]     at Promise._settlePromiseFromHandler (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/promise.js:512:31)
[0]     at Promise._settlePromise (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/promise.js:569:18)
[0]     at Promise._settlePromise0 (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/promise.js:614:10)
[0]     at Promise._settlePromises (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/promise.js:689:18)
[0]     at Async._drainQueue (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/async.js:133:16)
[0]     at Async._drainQueues (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/async.js:143:10)
[0]     at Immediate.Async.drainQueues [as _onImmediate] (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/async.js:17:14)
[0]     at runCallback (timers.js:756:18)
[0]     at tryOnImmediate (timers.js:717:5)
[0]     at processImmediate [as _immediateCallback] (timers.js:697:5)
[0]   name: 'ResponseError',
[0]   request:
[0]    Movie {
[0]      method: 'GET',
[0]      path: '/movie/24428',
[0]      timeout: 10000,
[0]      ensureHttps: false,
[0]      external_id: '24428' },
[0]   statusCode: 429 }

所以主要问题是 array.forEach 是一个同步函数,不会等待 client.send 完成。 一种解决方案是使用 bluebird.mapSeries (http://bluebirdjs.com/docs/api/promise.mapseries.html) 映射数组并等待每次迭代完成。 也不要忘记 return send-promise,所以 mapSeries-function 会知道它何时解决,因此它知道何时开始下一次迭代:

bluebird.mapSeries(files, function(file){
  return send(...)
}

最后一个建议是将整个 .then(... setTimeout...) 部分替换为 .delay(250)。 Request-Promise 已经使用了 bluebird promises,因此您可以使用它们的便捷功能 http://bluebirdjs.com/docs/api/delay.html。延迟会自动解析之前promise的值

return rp(options)
   .then(this._parseResponse)
   .delay(250)
   .catch(...)