How to avoid long nesting of asynchronous functions in Node.js

Solution 1:

Interesting observation. Note that in JavaScript you can normally replace inline anonymous callback functions with named function variables.

The following:

http.createServer(function (req, res) {
   // inline callback function ...

   getSomeData(client, function (someData) {
      // another inline callback function ...

      getMoreData(client, function(moreData) {
         // one more inline callback function ...
      });
   });

   // etc ...
});

Could be rewritten to look something like this:

var moreDataParser = function (moreData) {
   // date parsing logic
};

var someDataParser = function (someData) {
   // some data parsing logic

   getMoreData(client, moreDataParser);
};

var createServerCallback = function (req, res) {
   // create server logic

   getSomeData(client, someDataParser);

   // etc ...
};

http.createServer(createServerCallback);

However unless you plan to reuse to callback logic in other places, it is often much easier to read inline anonymous functions, as in your example. It will also spare you from having to find a name for all the callbacks.

In addition note that as @pst noted in a comment below, if you are accessing closure variables within the inner functions, the above would not be a straightforward translation. In such cases, using inline anonymous functions is even more preferable.

Solution 2:

Kay, simply use one of these modules.

  • flow-js
  • funk
  • futures
  • groupie
  • node-continuables
  • Slide
  • Step
  • node-inflow
  • async.js
  • async

It will turn this:

dbGet('userIdOf:bobvance', function(userId) {
    dbSet('user:' + userId + ':email', '[email protected]', function() {
        dbSet('user:' + userId + ':firstName', 'Bob', function() {
            dbSet('user:' + userId + ':lastName', 'Vance', function() {
                okWeAreDone();
            });
        });
    });
});

Into this:

flow.exec(
    function() {
        dbGet('userIdOf:bobvance', this);

    },function(userId) {
        dbSet('user:' + userId + ':email', '[email protected]', this.MULTI());
        dbSet('user:' + userId + ':firstName', 'Bob', this.MULTI());
        dbSet('user:' + userId + ':lastName', 'Vance', this.MULTI());

    },function() {
        okWeAreDone()
    }
);