Как регулировать Promise.all () до 5 обещаний в секунду? - PullRequest
0 голосов
/ 27 декабря 2018

У меня есть пара элементов, для которых мне нужно запросить сторонний API, и указанный API имеет ограничение вызовов в 5 вызовов в секунду.Мне нужно как-то уменьшить свои вызовы API до максимум 5 вызовов в секунду.

До сих пор я только что использовал Promise.all() в массиве обещаний, где каждое обещание отправляет запрос в APIи разрешает, когда API отвечает кодом состояния HTTP 200, и отклоняет, когда он отвечает другим кодом состояния.Однако, когда у меня более 5 элементов в массиве, я рискую отклонить Promise.all().

Как я могу ограничить вызов Promise.all() 5 вызовами в секунду?

Ответы [ 3 ]

0 голосов
/ 06 января 2019

Вы можете использовать параметр параллелизма в 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});
0 голосов
/ 22 августа 2019

Использование ES6 без библиотек

export async function asyncForEach(array, callback) {
  for (let index = 0; index < array.length; index++) {
    await callback(array[index], index, array);
  }
}
export function split(arr, n) {
  var res = [];
  while (arr.length) {
    res.push(arr.splice(0, n));
  }
  return res;
}
export const delayMS = (t = 200) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(t);
    }, t);
  });
};
export const throttledPromises = (
  asyncFunction,
  items = [],
  batchSize = 1,
  delay = 0
) => {
  return new Promise(async (resolve, reject) => {
    const output = [];
    const batches= split(items, batchSize);
    await asyncForEach(batches, async (batch) => {
      const promises = batch.map(asyncFunction).map(p => p.catch(reject));
      const results = await Promise.all(promises);
      output.push(...results);
      await delayMS(delay);
    });
    resolve(output);
  });
};
0 голосов
/ 27 декабря 2018

Я надеюсь, что это поможет вам.

А также следует сказать, что для разрешения всех запросов будет использоваться Promise.all, и если у вас большой список запросов, это подождет, пока все решат, и можетвызвать много ожидания в вашем коде, чтобы получить все ответы.А также, если один из запросов отклоняется, Promise.all отклонит.

Я предлагаю, если вам не нужны все результаты вместе, лучше использовать что-то еще, например lodash debounce или или рамки, которые справляются с этим.

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
...