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]
})