For more tips like this, sign up to the weekly newsletter!

Run promises sequentially

Promises provide an easy way to handle concurrency in JS. When you create multiple Promises, they run in parallel, as long as the single-thread model allows.

There is even a built-in function to collect the results of parallel promises:

const makePromise = (e) => {...}; // returns a Promise
const list = [...];

Promise.all(list.map(makePromise))
  then((results) => {
    // results contains all the results
  });

But how to run Promises strictly sequentially?

When a Promise is created, it starts executing. Therefore, the solution is to create the next Promise only when the previous one finishes.

Use reduce to chain them this way:

list.reduce((prev, e) => {
  return prev.then(() => makePromise(e));
}, Promise.resolve())
  .then(() => {
    // Called when all Promises resolve
  })

How does it work?

To demonstrate visually, let's consider an example! For a list of [1, 2, 3], it produces the following chain:

Promise.resolve()
  .then(() => makePromise(1))
  .then(() => makePromise(2))
  .then(() => makePromise(3))

Now it's easy to see why these resolve sequentially. We only make a new Promise when the previous one resolves.

Collecting the results

The above approach runs the promises in a sequence, but it does not provide an easy way to collect the results.

To fix this shortcoming, a little bit of magic is needed:

list.reduce((prev, e) => {
  return prev.then((partial) => makePromise(e).then((r) => [...partial, r]));
}, Promise.resolve([]))

It starts with an empty array and builds up the partial result when the Promises resolve.

To continue the example above, for the [1, 2, 3] list it produces the following result:

Promise.resolve([])
  .then((partial) => makePromise(1).then((result1) => [...partial, result1])) // partial is [ ]
  .then((partial) => makePromise(2).then((result2) => [...partial, result2])) // partial is [result1]
  .then((partial) => makePromise(3).then((result3) => [...partial, result3])) // partial is [result1, result2]
  .then((results) => {
    // results is [result1, result2, result3]
  })
Try it
References
Learn more: