Search code examples
node.jsasynchronouspromisees6-promisesynchronous

Nodejs - Making a very large 'X' amount of calls to a function that returns a Promise one at a time


I have a use case where I need to call an asynchronous NPM library many (>100,000 times) in a serial manner. This is for a compliance/research-related task in a program that will be running overnight (runtime is not super important). I am not able to make concurrent calls to the library from the same machine. That being said, I do not need to waterfall previous responses into later ones...the requests are completely independent of each other with the only constraint being that the library cannot handle concurrent requests from the same machine.

At a high level, I have my NPM library call as a function returning a Promise. My issue is that I do not know how to write a program that will handle sending that many requests one at a time in an automated fashion. If I were dealing with a known number of small (<5) requests, I could simply explicitly chain them together with .then() statements or even use a callback nest instead of Promises. If it were a larger, unknown number much less than the N > 100,000 I am dealing with now, I would write a function that could recursively call itself in the .then(). The recursive approach is what I'm doing now, which is suitable for a sample size of around 500 but will never work going 100,000 or more deep. My question is: How can I write a program that will automatically handle making these requests in a synchronous fashion without using recursion?

Here is an abstracted sample of the recursive solution I am currently using for my much smaller sample size of requests. inputs is a list containing information for each individual NPM call. Each item in inputs corresponds with an call to the NPM library that I must make.

let recursiveRunner = (index) => {
  // basecase
  if (index >= inputs.length) {
    // do stuff at end
    return;
  }

  let input = inputs[index];
  myPromisifiedNpmCall(input)
    .then(result => {
        // store my result appropriately
        someStoreResultFunction(result)
        // recursive call
        recursiveRunner(index + 1);
    })
    .catch(err => {
        // do stuff here
    });
};

// kick off recursiveRunner
recursiveRunner(0)

Solution

  • It seems there's no need for recursion, this can be solved with simple loop. This is a perfect use case for async..await that allows synchronous-like control flow:

    async function runner() {
      for (let i = 0; i < inputs.length; i++) {
        try {
          const input = inputs[i];
          const result = await myPromisifiedNpmCall(input);
          someStoreResultFunction(result);
        } catch (err) {...}
      }
    }
    

    Which is roughly equivalent to less resource efficient ES6 code:

    function runner() {
      let promise = Promise.resolve();
    
      for (let i = 0; i < inputs.length; i++) {    
        promise = promise
        .then(() => {
          const input = inputs[i];
          return myPromisifiedNpmCall(input);
        })
        .then(result => {
          someStoreResultFunction(result);
        })
        .catch(err => {...});
      }
    
      return promise;
    }