Promises in Serial Using Reduce

Published Dec 02, 2017Last updated May 25, 2018

In an earlier post, I showed how to run a series of promises in serial and in batches of 5 (or any arbitrary batch size). Today, I want to show how to do this with reduce.

We will be using the same function that mocks our interactions with an external service:

const requestToExternalService = function(d) {
  // Replace with a promise that does real work here...
  return new Promise(resolve => {
    console.log(d);

    // Delay demonstrates we are indeed batching
    setTimeout(resolve, 250);
  });
};

We can use reduce like this to run multiple promises in serial:

// Replace with real data
const data = [...Array(5).keys()];
const promiseChain = data.reduce(
  (chain, d) => chain.then(() => requestToExternalService(d)),
  Promise.resolve()
);

We can also use reduce to batch the promises 5 at a time, but first we will need a helper function to break down our work:

const chunk = (array, batchSize = 5) => {
  const chunked = [];
  for(let i = 0; i < array.length; i += batchSize) {
    chunked.push(array.slice(i, i + batchSize))
  }

  return chunked;
}

console.log(chunk([...Array(21).keys()]));
/*
 * [ [ 0, 1, 2, 3, 4 ],
 *   [ 5, 6, 7, 8, 9 ],
 *   [ 10, 11, 12, 13, 14 ],
 *   [ 15, 16, 17, 18, 19 ],
 *   [ 20 ] ]
*/

With it, we can now use reduce to create our promise chain:

// Replace with real data
const chunkedData = chunk([...Array(21).keys()]);
const reducer = (chain, batch) => chain
  .then(() => Promise.all(
    batch.map(d => requestToExternalService(d))
  ));

const promiseChain = chunkedData.reduce(
  reducer,
  Promise.resolve()
);

Try it yourself:

(Reposted)

Discover and read more posts from Mark Tse
get started