In JavaScript, entering callback hell or the pyramid of doom is generally considered to be a bad idea. Naming functions and keeping code shallow will keep you out of trouble. On it’s own, however, this doesn’t change the fact that you have to pass callback arguments as inputs to your functions. Promises solve that problem and more. In particular they compose nicely and help you handle errors with minimal repetition.

Below is a module that exposes three functions. Function getA takes no arguments and returns 'a'. Function getB takes the argument 'a' and returns 'b'. Function getC takes two arguments 'a' and 'b' and returns 'c'. From the perspective of client code, getA has no dependencies, getB depends on getA and getC depends on both getA and getB. If the arguments are not as expected a rejected promise is returned.

/*jslint node:true*/
'use strict';

/* global -Promise */
var Promise = require('promise');

module.exports.getA = function() {
  return Promise.resolve('a');
};

module.exports.getB = function(a) {
  if (a === 'a') return Promise.resolve('b');
  else return Promise.reject(new Error('a required'));
};

module.exports.getC = function(a, b) {
  if (a === 'a' && b === 'b') return Promise.resolve('c');
  else return Promise.reject(new Error('a and b  required'));
};

Composing getA and getB is easy assuming all you care about is 'b'. This is especially true if you use function pointers (you do right?). The code below shows how. And of course err is handled just once.

/*jslint node:true*/
'use strict';

var should = require('should');
var functions = require('../lib/functions');

/* global describe, it */
describe('promise', function () {
  it('should compose', function(done) {
    functions.getA().
      then(functions.getB).
      then(function(b) {
        b.should.be.exactly('b');
        done();
      }, function (err) {
        done(err);
      });
  });
});

Composing getA, getB and getC is a bit harder even assuming all you care about is 'c'. If it wasn’t for the depencencies between the functions then we could just use Promise#all and be done with it. But given the dependencies Promise#all won’t cut it. The code below shows how to do it.

/*jslint node:true*/
'use strict';

var should = require('should');
var functions = require('../lib/functions');

/* global describe, it */

describe('promise', function () {
  it('should compose', function(done) {
    functions.getA().then(function (a) {
      return functions.getB(a).then(function (b) {
        return functions.getC(a, b);
      });
    }).then(function (c) {
      c.should.be.exactly('c');
      done();
    }, function (err) {
      done(err);
    });
  });
});

Notice, however, that some nesting has crept in and there is a distinct lack of function pointers. What we really want is some function that can wrap getB and getC whilst carrying over the results of each invocation to the next function. The usage of such a carry function is shown below.

/*jslint node:true*/
'use strict';

var should = require('should');
var carry = require('../lib/carry');
var functions = require('../lib/functions');

/* global describe, it */
describe('carry', function () {
  it('should carry results over', function(done) {
    functions.getA().
      then(carry(functions.getB)).
      then(carry(functions.getC)).
      then(carry(function(a, b, c) {
        a.should.be.exactly('a');
        b.should.be.exactly('b');
        c.should.be.exactly('c');
        done();
      }), function (err) {
        done(err);
      });
  });
});

The carry function reduces nesting, allows us to use function pointers and gives us to access 'a', 'b' and 'c'. The code below shows how to define it.

/*jslint node:true*/
'use strict';

module.exports = function(f) {
  return function(data) {
    if (data && data.constructor === Array) {
      return f.apply(this, data).then(function (result) {
        if (result && result.constructor === Array) {
          return data.concat(result);
        } else {
          return data.concat([result]);
        }
      });
    } else {
      return f(data).then(function (result) {
        return [data, result];
      });
    }
  };
};

It’s worth noting that this isn’t the only way to solve this particular problem. In fact, I spent a lot of time thrashing about before I settled on this as my favourite pattern.