Reduce an array to chained promises
suggest changeThis design pattern is useful for generating a sequence of asynchronous actions from a list of elements.
There are two variants :
- the “then” reduction, which builds a chain that continues as long as the chain experiences success.
- the “catch” reduction, which builds a chain that continues as long as the chain experiences error.
The “then” reduction
This variant of the pattern builds a .then()
chain, and might be used for chaining animations, or making a sequence of dependent HTTP requests.
[1, 3, 5, 7, 9].reduce((seq, n) => {
return seq.then(() => {
console.log(n);
return new Promise(res => setTimeout(res, 1000));
});
}, Promise.resolve()).then(
() => console.log('done'),
(e) => console.log(e)
);
// will log 1, 3, 5, 7, 9, 'done' in 1s intervals
Explanation:
- We call
.reduce()
on a source array, and providePromise.resolve()
as an initial value. - Every element reduced will add a
.then()
to the initial value. reduce()
’s product will be Promise.resolve().then(…).then(…).- We manually append a
.then(successHandler, errorHandler)
after the reduce, to executesuccessHandler
once all the previous steps have resolved. If any step was to fail, thenerrorHandler
would execute.
Note: The “then” reduction is a sequential counterpart of Promise.all()
.
The “catch” reduction
This variant of the pattern builds a .catch()
chain and might be used for sequentially probing a set of web servers for some mirrored resource until a working server is found.
var working_resource = 5; // one of the values from the source array
[1, 3, 5, 7, 9].reduce((seq, n) => {
return seq.catch(() => {
console.log(n);
if(n === working_resource) { // 5 is working
return new Promise((resolve, reject) => setTimeout(() => resolve(n), 1000));
} else { // all other values are not working
return new Promise((resolve, reject) => setTimeout(reject, 1000));
}
});
}, Promise.reject()).then(
(n) => console.log('success at: ' + n),
() => console.log('total failure')
);
// will log 1, 3, 5, 'success at 5' at 1s intervals
Explanation:
- We call
.reduce()
on a source array, and providePromise.reject()
as an initial value. - Every element reduced will add a
.catch()
to the initial value. reduce()
’s product will bePromise.reject().catch(...).catch(...)
.- We manually append
.then(successHandler, errorHandler)
after the reduce, to executesuccessHandler
once any of the previous steps has resolved. If all steps were to fail, thenerrorHandler
would execute.
Note: The “catch” reduction is a sequential counterpart of Promise.any()
(as implemented in bluebird.js
, but not currently in native ECMAScript).
Found a mistake? Have a question or improvement idea?
Let me know.
Table Of Contents