It’s pretty straightforward with some simple rules:
- Whenever you create a promise in a
then
, return it – any promise you don’t return will not be waited for outside. - Whenever you create multiple promises,
.all
them – that way it waits for all the promises and no error from any of them are silenced. - Whenever you nest
then
s, you can typically return in the middle –then
chains are usually at most 1 level deep. - Whenever you perform IO, it should be with a promise – either it should be in a promise or it should use a promise to signal its completion.
And some tips:
- Mapping is better done with
.map
than withfor/push
– if you’re mapping values with a function,map
lets you concisely express the notion of applying actions one by one and aggregating the results. - Concurrency is better than sequential execution if it’s free – it’s better to execute things concurrently and wait for them
Promise.all
than to execute things one after the other – each waiting before the next.
Ok, so let’s get started:
var items = [1, 2, 3, 4, 5];
var fn = function asyncMultiplyBy2(v){ // sample async action
return new Promise(resolve => setTimeout(() => resolve(v * 2), 100));
};
// map over forEach since it returns
var actions = items.map(fn); // run the function over all items
// we now have a promises array and we want to wait for it
var results = Promise.all(actions); // pass array of promises
results.then(data => // or just .then(console.log)
console.log(data) // [2, 4, 6, 8, 10]
);
// we can nest this of course, as I said, `then` chains:
var res2 = Promise.all([1, 2, 3, 4, 5].map(fn)).then(
data => Promise.all(data.map(fn))
).then(function(data){
// the next `then` is executed after the promise has returned from the previous
// `then` fulfilled, in this case it's an aggregate promise because of
// the `.all`
return Promise.all(data.map(fn));
}).then(function(data){
// just for good measure
return Promise.all(data.map(fn));
});
// now to get the results:
res2.then(function(data){
console.log(data); // [16, 32, 48, 64, 80]
});