Chaining promises

How to connect promises into single sequence.

Situation

Need to call several functions one after another (waterfall sequence). Each function should pass an argument to the next

1
2
3
4
5
6
7
8
9
10
11
12
13
var Q = require('Q');
function foo(arg) {
console.log('foo arg=' + arg);
var d = Q.defer();
process.nextTick(function () { d.resolve(arg); });
return d.promise;
}
function bar(arg) {
console.log('bar arg=' + arg);
var d = Q.defer();
process.nextTick(function () { d.resolve(arg); });
return d.promise;
}

Manual sequence

Connect the promise-returning functions into a chain using .then method

foo('a').then(bar).done();

Prints correctly the argument a passed through.

foo arg=a
bar arg=a

I always use .done() method to make sure any exceptions inside the chain are not hidden.

Dynamic chains

Q promise library has an utility method for dealing with sequences. The first rule: place all functions that return promises into an array. Then create the chain using Array.reduce method

[foo, bar].reduce(function (chain, fn) {
  return chain.then(fn);
}, Q('a')).done();
// prints
foo arg=a
bar arg=a

Notice that we pass the initial argument via the second argument Q('a') to reduce call.

Shorthand

Writing the reduce chaining is boilerplate, and Q comes with Q.when that calls .then for you and allows much shorter sequence notation:

[foo, bar].reduce(Q.when, Q('a')).done();
// prints
foo arg=a
bar arg=a

Starting things right

Passing the argument to foo via Q('a') is weird. Let's shift the functions around to make the syntax more natural. The purpose of Q('a') is to start the chain. Well we want to start the chain with calling foo! So we can move first function from the array of promise-returning functions into the reduce

1
[bar].reduce(Q.when, foo('a')).done();

It looks a little weird, but usually you have more than two functions to chain, and the syntax would look better.