Once I got comfortable writing asynchronous code using promises, I started connecting multiple promises into chains. There are several patterns I used over and over. This is a short list of common ones.
Always using .done
One of the best advantages of using promises is their error handling.
The errors can be handled separately from the main code, without
a second pyramid of doom of nested callbacks. Unfortunately there
is a catch: if a chain of promises does not have .done
at the end,
an exception can be quietly swallowed by the promise function.
That is why I
always use .done
to make sure any unhandled errors are rethrown.
Avoid creating unnecessary promises
You can always connect to an existing promise avoiding creating an unnecessary promise. For example, instead of this:
1 | // function bar returns a promise |
You can directly return the original promise returned by bar
with
attached extra step:
1 | // function bar returns a promise |
Notice that if a function inside .then
returns a value, it will become new resolved
value for the original promise. If nothing is returned, then the original
value is passed to next steps.
Single value
The promises are simple in part because they are resolved or rejected
with single a value: foo().then(function (value) {...})
.
If you need to pass additional known arguments to callbacks, you can
use function binding:
1 | function printResult(message, value) { |
EcmaScript5 function.bind
binds arguments left to right, so the resolved
value should always be the last one. Libraries such as
lodash provide partial application
functions that bind arguments left to right or right to left if needed.
1 promise
Here are two functions I am going to reuse in the next examples
1 | // code to be reused for several examples |
Here is a single promise connected to a function, the simplest promise use case:
1 | getNumber(1).then(function (value) { |
2 promises in parallel into array
If you need to execute multiple promises in parallel (well, not really in parallel, since JavaScript is single-threaded, more like logically parallel), you can use Q.all method that returns an array of resolved values.
1 | q.all([getNumber(2), getNumber(3)]).then(function (value) { |
If using Q.all
seems like too much typing, you can use Q()
shorthand
notation: q([getNumber(2), getNumber(3)]).then ...
.
Both Q.all()
and Q()
require a single Array instance as argument, which is
different from jQuery.when that accepts
variable number of deferreds instead of an array.
2 promises in parallel
If using Q.all
to start the chain, I prefer to use .spread
instead of .then
.
It spreads the resolved values into separate variables, instead of resolving them
into single Array argument.
1 | q.all([getNumber(2), getNumber(3)]).spread(function (a, b) { |
Multiple promises in sequence
When making multiple requests, you might need to execute one
promise after another. You can either construct a chain of promises manually
bar().then(foo).then ...
or
reduce an array of promise-returning functions,
eventually writing a very neat one liner.
1 | var get2 = getNumber.bind(null, 2); |
It is easy to get the last value of the sequence, but if you need all resolved values, you would have to use an external data object and keep track yourself.