Node.js

async.js

Syntax#

  • Each callback must be written with this syntax:

  • function callback(err, result [, arg1[, …]])

  • This way, you are forced to return the error first, and can’t ignore handling them later on. null is the convention in absence of errors

  • callback(null, myResult);

  • Your callbacks can contain more arguments than err and result, but it’s useful only for a specific set of functions (waterfall, seq, …)

  • callback(null, myResult, myCustomArgument);

  • And, of course, send errors. You must do it, and handle errors (or at least log them).

  • callback(err);

Parallel : multi-tasking

async.parallel(tasks, afterTasksCallback) will execute a set of tasks in parallel and wait the end of all tasks (reported by the call of callback function).

When tasks are finished, async call the main callback with all errors and all results of tasks.

function shortTimeFunction(callback) {
  setTimeout(function() {
    callback(null, 'resultOfShortTime');
  }, 200);
}

function mediumTimeFunction(callback) {
  setTimeout(function() {
    callback(null, 'resultOfMediumTime');
  }, 500);
}

function longTimeFunction(callback) {
  setTimeout(function() {
    callback(null, 'resultOfLongTime');
  }, 1000);
}

async.parallel([
    shortTimeFunction,
    mediumTimeFunction,
    longTimeFunction
  ],
  function(err, results) {
    if (err) {
      return console.error(err);
    }

    console.log(results);
  });

Result : ["resultOfShortTime", "resultOfMediumTime", "resultOfLongTime"].

Call async.parallel() with an object

You can replace the tasks array parameter by an object. In this case, results will be also an object with the same keys than tasks.

It’s very useful to compute some tasks and find easily each result.

async.parallel({
    short: shortTimeFunction,
    medium: mediumTimeFunction,
    long: longTimeFunction
  },
  function(err, results) {
    if (err) {
      return console.error(err);
    }

    console.log(results);
  });

Result : {short: "resultOfShortTime", medium: "resultOfMediumTime", long: "resultOfLongTime"}.

Resolving multiple values

Each parallel function is passed a callback. This callback can either return an error as the first argument or success values after that. If a callback is passed several success values, these results are returned as an array.

async.parallel({
    short: function shortTimeFunction(callback) {
      setTimeout(function() {
        callback(null, 'resultOfShortTime1', 'resultOfShortTime2');
      }, 200);
    },
    medium: function mediumTimeFunction(callback) {
      setTimeout(function() {
        callback(null, 'resultOfMediumTime1', 'resultOfMeiumTime2');
      }, 500);
    }
  },
  function(err, results) {
    if (err) {
      return console.error(err);
    }

    console.log(results);
  });

Result :

{
    short: ["resultOfShortTime1", "resultOfShortTime2"], 
    medium: ["resultOfMediumTime1", "resultOfMediumTime2"]
}

.

Series : independent mono-tasking

async.series(tasks, afterTasksCallback) will execute a set of tasks. Each task are executed after another. If a task fails, async stops immediately the execution and jump into the main callback.

When tasks are finished successfully, async call the “master” callback with all errors and all results of tasks.

function shortTimeFunction(callback) {
  setTimeout(function() {
    callback(null, 'resultOfShortTime');
  }, 200);
}

function mediumTimeFunction(callback) {
  setTimeout(function() {
    callback(null, 'resultOfMediumTime');
  }, 500);
}

function longTimeFunction(callback) {
  setTimeout(function() {
    callback(null, 'resultOfLongTime');
  }, 1000);
}

async.series([
    mediumTimeFunction,
    shortTimeFunction,
    longTimeFunction
  ],
  function(err, results) {
    if (err) {
      return console.error(err);
    }

    console.log(results);
  });

Result : ["resultOfMediumTime", "resultOfShortTime", "resultOfLongTime"].

Call async.series() with an object

You can replace the tasks array parameter by an object. In this case, results will be also an object with the same keys than tasks.

It’s very useful to compute some tasks and find easily each result.

async.series({
    short: shortTimeFunction,
    medium: mediumTimeFunction,
    long: longTimeFunction
  },
  function(err, results) {
    if (err) {
      return console.error(err);
    }

    console.log(results);
  });

Result : {short: "resultOfShortTime", medium: "resultOfMediumTime", long: "resultOfLongTime"}.

Waterfall : dependent mono-tasking

async.waterfall(tasks, afterTasksCallback) will execute a set of tasks. Each task are executed after another, and the result of a task is passed to the next task. As async.series(), if a task fails, async stop the execution and call immediately the main callback.

When tasks are finished successfully, async call the “master” callback with all errors and all results of tasks.

function getUserRequest(callback) {
  // We simulate the request with a timeout
  setTimeout(function() {
    var userResult = {
      name : 'Aamu'
    };

    callback(null, userResult);
  }, 500);
}

function getUserFriendsRequest(user, callback) {
  // Another request simulate with a timeout
  setTimeout(function() {
    var friendsResult = [];

    if (user.name === "Aamu"){
        friendsResult = [{
          name : 'Alice'
        }, {
          name: 'Bob'
        }];
    }
    
    callback(null, friendsResult);
  }, 500);
}

async.waterfall([
    getUserRequest,
    getUserFriendsRequest
  ],
  function(err, results) {
    if (err) {
      return console.error(err);
    }

    console.log(JSON.stringify(results));
  });

Result: results contains the second callback parameter of the last function of the waterfall, which is friendsResult in that case.

async.times(To handle for loop in better way)

To execute a function within a loop in node.js, it’s fine to use a for loop for short loops. But the loop is long, using for loop will increase the time of processing which might cause the node process to hang. In such scenarios, you can use: asycn.times

function recursiveAction(n, callback)
{
    //do whatever want to do repeatedly
    callback(err, result);
}
async.times(5, function(n, next) {
    recursiveAction(n, function(err, result) {
        next(err, result);
    });
}, function(err, results) {
    // we should now have 5 result
});

This is called in parallel. When we want to call it one at a time, use: async.timesSeries

async.each(To handle array of data efficiently)

When we want to handle array of data, its better to use async.each. When we want to perform something with all data & want to get the final callback once everything is done, then this method will be useful. This is handled in parallel way.

function createUser(userName, callback)
{
    //create user in db
    callback(null)//or error based on creation
}

var arrayOfData = ['Ritu', 'Sid', 'Tom'];
async.each(arrayOfData, function(eachUserName, callback) {

    // Perform operation on each user.
    console.log('Creating user '+eachUserName);
    //Returning callback is must. Else it wont get the final callback, even if we miss to return one callback
    createUser(eachUserName, callback);
  
}, function(err) {
    //If any of the user creation failed may throw error.
    if( err ) {
      // One of the iterations produced an error.
      // All processing will now stop.
      console.log('unable to create user');
    } else {
      console.log('All user created successfully');
    }
});

To do one at a time can use async.eachSeries

async.series(To handle events one by one)

/In async.series,all the functions are executed in series and the consolidated outputs of each function is passed to the final callback. e.g/

var async = require(‘async’); async.series([ function (callback) { console.log(‘First Execute..’); callback(null, ‘userPersonalData’); }, function (callback) { console.log(‘Second Execute.. ’); callback(null, ‘userDependentData’); } ], function (err, result) { console.log(result); });

//Output:

First Execute.. Second Execute.. [‘userPersonalData’,‘userDependentData’] //result


This modified text is an extract of the original Stack Overflow Documentation created by the contributors and released under CC BY-SA 3.0 This website is not affiliated with Stack Overflow