Promises are really simple to use when passing a single argument from the first operation to the next one. What if we need to pass multiple arguments? For example, imagine we need to generate two numbers and then sum them up, all in an asynchronous manner. We can write these simple functions to start:
1 | var Q = require('q'); |
If we need to perform each operation in sequence, we get back to the original pyramid of doom of nested callbacks.
1 | getA() |
The pyramid is necessary to keep a
and b
in the lexical scope for the last step (calling sum(a, b)
).
We can simplify the pyramid a little if we are allowed to run getA
and getB
in parallel
1 | Q.all([getA(), getB()]) |
We are using Q.all method
to execute multiple promises in parallel and get a single promise back that resolves with an array.
The ab
array passed to the next step has the result of each individual promise in the same order as the promises
passed to Q.all([...])
.
Taking it one step further, there is
Q.spread
that can be used after Q.all
to separate results into individual arguments.
1 | Q.all([getA(), getB()]) |
Think of Q.all().then(fn)
as executing fn.call(null, [results])
, while Q.all().spread(fn)
as
executing fn.apply(null, results)
.
We can even skip the intermediate function and call the sum
directly using
point-free style
1 | Q.all([getA(), getB()]) |
Using the original pure function, sum
is very convenient, because we can still apply
the partial application to prefill some arguments. For example to always add 2 to the number returned from getA
we can use either the initial step or the partial application
1 | Q.all([getA(), 10]) |
1 | Q.all([getA()]) |
Accumulating variables
Let us go back to the original sequence where we executed getA
and getB
in sequence.
We can use Q.all
and Q.spread
to pass extra arguments to the next step without using an object or
an array.
1 | getA() |
All we have done here was to push the promise returned by getB
to the array returned by Q.all
, which
then we spread into sum
arguments. Compare this solution to the original pyramid-like solution at the beginning
of this post. We are not relying on the lexical scope any more, and we keep the promise chain very flat.
Imagine we want to print both operands and the result instead of printing the result only.
1 | getA() |
Update - ES6
In ES6 we can combine Promise.all method with parameter destructuring to place the results into separate named variables in a single command
1 | Promise.all([p1, p2, p3]) |