如何将Promise.all()限制为每秒5个承诺?

问题描述 投票:2回答:2

我有几个项目需要查询第三方API,并且API的调用限制为每秒5次调用。我需要以某种方式限制我对API的调用,每秒最多5次调用。

到目前为止,我刚刚在一系列promises中使用了Promise.all(),其中每个promise向API发送请求,并在API响应HTTP状态代码200时解析,并在响应其他状态代码时拒绝。但是,当我在阵列中有超过5个项目时,我冒险Promise.all()拒绝。

如何将Promise.all()呼叫限制为每秒5次呼叫?

javascript node.js promise es6-promise throttling
2个回答
2
投票

我希望这会对你有所帮助。

并且还要说这将使用Promise.all来解决所有请求,如果你有一个大的查询列表,这将等待所有解决,并可能导致很多等待你的代码获得所有响应。如果其中一个请求拒绝,Promise.all将拒绝。

我建议如果你不需要所有的结果,最好使用其他东西,如lodash debouncethrottle或处理这个的框架。

let items = [
    {name: 'item1'}, 
    {name: 'item2'}, 
    {name: 'item3'}, 
    {name: 'item4'}, 
    {name: 'item5'}, 
    {name: 'item6'}
];

// This is the api request that you send and return a promise
function apiCall(item) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(item.name), 1000);
  })
}

new Promise((resolve) => {
  let results = [];

  function sendReq (itemsList, iterate, apiCall) {
    setTimeout(() => {
      // slice itemsList to send request according to the api limit
      let slicedArray = itemsList.slice(iterate * 5, (iterate * 5 + 5));
      result = slicedArray.map(item => apiCall(item));
      results = [...results, ...result];

      // This will resolve the promise when reaches to the last iteration
      if (iterate === Math.ceil(items.length / 5) - 1) {
          resolve(results);
      }
    }, (1000 * iterate)); // every 1000ms runs (api limit of one second)
  }

  // This will make iteration to split array (requests) to chunks of five items 
  for (i = 0; i < Math.ceil(items.length / 5); i++) {
    sendReq(items, i, apiCall);
  }
}).then(Promise.all.bind(Promise)).then(console.log);
// Use Promise.all to wait for all requests to resolve
// To use it this way binding is required

3
投票

如果你不太担心按顺序解析promises,你可以在bluebird中使用并发选项。

以下将仅处理5个查询。

const Promise = require('bluebird');

const buildQueries = (count) => {
  let queries = [];

  for(let i = 0; i < count; i++) {
    queries.push({user: i});
  };

  return queries;
};

const apiCall = (item) => {
  return new Promise(async (resolve, reject) => {
    await Promise.delay(1000);
    resolve(item.user);
  });
};

const queries = buildQueries(20);

Promise.map(queries, async query => {
  console.log( await apiCall(query) );
}, {concurrency: 5});
© www.soinside.com 2019 - 2024. All rights reserved.