I am Hack Sparrow
Captain of the Internets.

Managing Nested Asynchronous Callbacks in Node.js

How to manage nested asynchronous callbacks in Node.js

Anyone who works with Node.js seriously will sooner or later come face to face with Node's infamous multiple nested asynchronous callbacks problem.

The nested asynchronous callbacks problem is where you have a callback function within a callback function within a callback function within a callback function within ... etc. Depending on the depth of nesting, it could get very ugly and dirty, even unmanageable. Take a look at this simple example:

// This script will check if a file named async.txt exists, if it doesn't, it will create it, and write the contents of test.js (this file) to async.txt
var fs = require('fs');

var path = './async.txt';
// check if async.txt exists
fs.stat(path, function(err, stats) {
    if (stats == undefined) {
        // read the contents of this file
        fs.readFile(__filename, function(err, content) {
            var code = content.toString();
            // write the content to async.txt
            fs.writeFile(path, code, function(err) {
                if (err) throw err;
                console.log('async.txt created!');
            });
        });    
    }
});

We have a three-level deep asynchronous callback function here. You might run into situations where it might be even much deeper than that.

One suggestion is to define the callback functions outside and call them from the callbacks, but it just dirties the namespace, and makes your code look ugly.

To manage such callback-within-a-callback problems, many modules have been developed to manage the asynchronous flow in Node.js. The top contenders for my personal choice were Async, and Step. I choose Async over Step because of its flexibility and features. Async can do what Step can, and more. Let's see what happens when we modify the last example using Async.

First install Async:

$ npm install async

And here is the rewrite of the last script using Async:

// This script will check if a file named async.txt exists, if it doesn't, it will create it, and write the contents of test.js (this file) to async.txt
var fs = require('fs');
var async = require('async');

var path = './async.txt';
async.waterfall([
    // check if async.txt exists
    function(cb) {
        fs.stat(path, function(err, stats) {
            if (stats == undefined) cb(null);
            else console.log('async.txt exists');
        });
    },
    // read the contents of this file
    function(cb) {
        fs.readFile(__filename, function(err, content) {
            var code = content.toString();
            cb(null, code);
        });
    },
    // write the content to async.txt
    function(code, cb) {
        fs.writeFile(path, code, function(err) {
            if (err) throw err;
            console.log('async.txt created!');
        });
    }
]);

Much better, isn't it? You can see the execution order, the callbacks don't get deeper indented (imagine if it was 10 levels deep) with each level; overall you have a much neater looking code.

Async has much more to it than what you see above. Till I write a tutorial on Async, you can visit the Async GitHub page to know more about it.

One Response to “Managing Nested Asynchronous Callbacks in Node.js”

  1. Coffeeyesplease says:

    Thank you, is precisely what I was looking for.
    My code was getting pretty horrible to read and for a C#/C++/Java programmer it was doing my head in. So thank you for bringing some organization back to my life.
    :-)

Make a Comment