Linking promises

How to connect multiple promise-returning methods into single chain.

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
2
3
4
5
6
7
8
9
10
11
// function bar returns a promise
function foo() {
var defer = Q.defer();
bar().then(function (value) {
defer.resolve('bar returns ' + value);
}, function (err) {
defer.reject(err);
});
return defer.promise;
}
foo().then(...)

You can directly return the original promise returned by bar with attached extra step:

1
2
3
4
5
6
7
// function bar returns a promise
function foo() {
return bar().then(function (value) {
return 'bar returns ' + value;
});
}
foo().then(...)

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
2
3
4
5
6
7
8
function printResult(message, value) {
console.log(message, value);
}
foo().then(printResult.bind(null, 'foo returns'));
// equivalent to
foo().then(function printResult('foo returns', 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
2
3
4
5
6
7
8
9
10
11
12
13
14
// code to be reused for several examples
// my favorite promise implementation Q
var q = require('q');
function getNumber(a) {
var defer = q.defer();
setTimeout(function () {
defer.resolve(a);
}, 1000 * Math.random());
return defer.promise;
}
// returns an array of promises
function getNumbers(a, b) {
return [getNumber(a), getNumber(b)];
}

Here is a single promise connected to a function, the simplest promise use case:

1
2
3
4
getNumber(1).then(function (value) {
console.log(value);
}).done();
// prints 1

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
2
3
4
q.all([getNumber(2), getNumber(3)]).then(function (value) {
console.log(value);
}).done();
// prints [2, 3]

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
2
3
4
q.all([getNumber(2), getNumber(3)]).spread(function (a, b) {
console.log(a, b);
}).done();
// prints 2, 3

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
2
3
4
5
6
7
var get2 = getNumber.bind(null, 2);
var get3 = getNumber.bind(null, 3);
[get2, get3].reduce(q.when, q())
.then(function (value) {
console.log('chain value =', value);
}).done();
// prints 3 (the last value)

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.