Dealing with the node.js callback pyramid

coding-stylenode.js

I've just started using node, and one thing I've quickly noticed is how quickly callbacks can build up to a silly level of indentation:

doStuff(arg1, arg2, function(err, result) {
    doMoreStuff(arg3, arg4, function(err, result) {
        doEvenMoreStuff(arg5, arg6, function(err, result) {
            omgHowDidIGetHere();
        });
    });
});

The official style guide says to put each callback in a separate function, but that seems overly restrictive on the use of closures, and making a single object declared in the top level available several layers down, as the object has to be passed through all the intermediate callbacks.

Is it ok to use function scope to help here? Put all the callback functions that need access to a global-ish object inside a function that declares that object, so it goes into a closure?

function topLevelFunction(globalishObject, callback) {

    function doMoreStuffImpl(err, result) {
        doMoreStuff(arg5, arg6, function(err, result) {
            callback(null, globalishObject);
        });
    }

    doStuff(arg1, arg2, doMoreStuffImpl);
}

and so on for several more layers…

Or are there frameworks etc to help reduce the levels of indentation without declaring a named function for every single callback? How do you deal with the callback pyramid?

Best Answer

Promises provide a clean separation of concerns between asynchronous behavior and the interface so asynchronous functions can be called without callbacks, and callback interaction can be done on the generic promise interface.

There is multiple implementations of "promises":


For example you can rewrite this nested callbacks

http.get(url.parse("http://test.com/"), function(res) {
    console.log(res.statusCode);
    http.get(url.parse(res.headers["location"]), function(res) {
        console.log(res.statusCode);
    });
});

like

httpGet(url.parse("http://test.com/")).then(function (res) {
    console.log(res.statusCode);
    return httpGet(url.parse(res.headers["location"]));
}).then(function (res) {
    console.log(res.statusCode);
});

Instead of callback in callback a(b(c())) you chain the ".then" a().then(b()).then(c()).


An introduction here: http://howtonode.org/promises

Related Topic