Using Map/Reduce with Asynchronous Operations in TypeScript
A common problem I've encountered is the need to work with a series of operations, all asynchronous, while maintaining ordering but knowing when the series is complete. To that end I wanted to share a pattern I've come to like and should be applicable across many types of scenarios.
First we start with an array of operations that will result in a Promise being returned. Likely an XHR request, but could also be a custom operation, or a file read in node.
function something(a: number): Promise<any> {
// ...
}
let operations: Promise<any>[] = [];
operations.push(something(1).then(a => {...}));
operations.push(something(2).then(a => {...}));
operations.push(something(3).then(a => {...}));
Now that we have a set of operations we need to let them finish, but be alerted when they are done. We can do this with the array's reduce method.
operations.reduce((chain, operation) => {
return chain.then(_ => operation);
}, Promise.resolve()).then(_ => { // All the operations are complete });
The initial value, given by Promise.resolve(), allows us to create a chain of promises that will resolve in order and let us know when the entire set if complete. There are other parameters you get in the reduce delegate signature: index and the target array, see the docs for full details. You can also use the map method to handle any pre-processing you might need in your array of operations.
operations.map(operation => {
// do something to operation
return operation;
}).reduce((chain, operation) => {
return chain.then(_ => operation);
}, Promise.resolve()).then(_ => {
// all the operations have finished
});
You can see this pattern in action in the JavaScript Core Libraries batching to reduce the batched operations into a single promise to allow the caller to know when the batched requests are all complete.
Hope folks find this helpful when working to resolve a set of operations which all return a promise.