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

时间:2018-02-24 20:09:14

标签: node.js promise request-promise

我正在尝试使用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客户端的片段返回请求承诺(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;
                });
}

Async不起作用,它返回“超出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 }

1 个答案:

答案 0 :(得分:1)

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

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

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

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