simonas88 simonas88 - 28 days ago 17
Javascript Question

How to iterate through an array of promises in node.js?

In javascript, I have an non flat array of Promises:

const array = [
[ promise11, promise12.. promise1n ],
[ promise21, promise22.. promise2n ],
[ promise31, promise32.. promise3n ],
...
[ promisek1, promisek2.. promisekn ]
];


How do iterate through the array in such a fashion, where the next array of promises would start resolving ONLY after the previous array of promises have resolved? For instance I would like the script to wait until
[ promise11, promise12.. promise1n ]
promises resolve in parrallel, before calling on to the next array of promises.

I am looking for a general solution as my application cannot know in advance how many calls it'll have to make (it's a webscrapper).

Answer Source

You could use a variation on the Array#reduce pattern:

array.reduce(
    (p, subArray) => p.then(() => Promise.all(subArray)),
    Promise.resolve()
);

That sets up a promise chain where each entry in the chain is the result of the promise from Promise.all for the subarrays in the array.

...but:

...where the next array of promises would start resolving ONLY after the previous array of promises have resolved?

That's not how promises work. Once you have the promise, the action it promises a result for is already in progress. You don't "start" it.

So instead, you'd want to make array an array of functions that start the processes in question and return promises, which is slightly different:

function get(label) {
  console.log(label + "- starting");
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(label + "- resolving");
      resolve(label);
    }, Math.random() * 500);
  });
}
const array = [
  [ get.bind(null, "1a"), get.bind(null, "1b"), get.bind(null, "1c") ],
  [ get.bind(null, "2a"), get.bind(null, "2b") ],
  [ get.bind(null, "3a"), get.bind(null, "3b"), get.bind(null, "3c"), get.bind(null, "3d") ]
];

array.reduce(
  (p, subArray) => p.then(() => Promise.all(subArray.map(f => f()))),
  Promise.resolve()
).then(() => {
  console.log("All done");
});
.as-console-wrapper {
  max-height: 100% !important;
}

Notice how the array is now an array of functions, which when called "start" the process and then return a promise. Our Array#reduce loop is only slightly modified: We use Array#map to call the functions for this subarray and get an array of their promises, which is what we feed into Promise.all. So all of the functions for a given subarray are called to start the work for that subarray, and then we wait unti they're all complete before continuing to the next subarray and having it start its work.