Search code examples
javascriptnode.jspromisees6-promisethrottling

How to throttle Promise.all() to 5 promises per second?


I have a couple of items that I need to query a 3rd party API for and said API has a call limit of 5 calls per second. I need to somehow throttle my calls to the API to a maximum of 5 calls per second.

So far I've just used Promise.all() on an array of promises, where each promise sends a request to the API and resolves when the API responds with the HTTP status code 200 and rejects when it responds with some other status code. However, when I have more than 5 items in the array, I risk that the Promise.all() rejects.

How can I limit the Promise.all() call to 5 calls per second?


Solution

  • I hope this would help you.

    And also to be said this would use Promise.all to resolve all requests and if you have a large list of queries, this would wait for all to resolve and may cause a lot waiting in your code to get all responses. And also if one of request rejects, Promise.all will reject.

    I suggest if you don't need all results together it's better to use something else like lodash debounce or throttle or frameworks that handle this.

    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