Managing callbacks using Async in Node.js

Managing callbacks using Async in Node.js

  • 2016-08-20
  • 2449

Callback hell is a wonder that distresses a JavaScript engineer when he tries to execute various nonconcurrent operations consistently. A few people call it to be the pyramid of doom.

We should observe a case of what we call callback damnation.

doSomething(param1, param2, function(err, paramx){
    doMore(paramx, function(err, result){
        insertRow(result, function(err){
            yetAnotherOperation(someparameter, function(s){
                somethingElse(function(x){
                });
            });
        });
    });
});

Note- The above code is for show and not a working code.

This is looking terrible just from the skeleton itself. In genuine code you will clearly have different articulations like if, for or some different operations alongside these settled capacities. Add those to the above code and it would get truly untidy and unmanageable.

Pro Tip

Try not to over-burden your capacities. The vast majority of the times it may be conceivable that the genuine issue you are confronting is not the callback hell, rather it’s ineffectively composed capacities. In such cases, the arrangements we will see beneath would structure your code however they won’t tackle your genuine issue. In this way, as a dependable guideline, make your capacity do less, which means compose little capacities that achieve a solitary undertaking.

Techniques for avoiding callback hell

There are numerous systems for managing callback damnation. In this instructional exercise, we will observe the beneath two specifically.

  1. Utilizing Async.js

  2. Utilizing Promises

Managing callbacks utilizing Async.js

Async is a truly capable npm module for overseeing offbeat nature of JavaScript. Alongside Node.js, it additionally works for JavaScript composed for programs.

Async gives loads of intense utilities to work with nonconcurrent forms under various situations.

Installation
npm install --save async
async.waterfall()

For our issue, we will take a gander at two elements of async specifically. i.e async.waterfall() and async.series().

Async Waterfall is valuable when you need to execute a few undertakings in a steady progression and in the meantime go on the outcome from the past assignment to the following.

async.waterfall() takes in a variety of capacities ‘tasks’ and a last ‘callback’ capacity which is called after every one of the capacities in tasks have finished or a callback is called with a mistake.

Observe underneath for better understanding.

Async Waterfall

var async = require('async');
async.waterfall([
    function(callback) {
        //doSomething
        callback(null, paramx); //paramx will be availaible as the first parameter to the next function
        /**
            The 1st parameter passed in callback.
            @null or @undefined or @false control moves to the next function
            in the array
            if @true or @string the control is immedeatly moved
            to the final callback fucntion
            rest of the functions in the array
            would not be executed
        */
    },
    function(arg1, callback) {
        //doSomething else
      // arg1 now equals paramx
        callback(null, result);
    },
    function(arg1, callback) {
        //do More
        // arg1 now equals 'result'
        callback(null, 'done');
    },
    function(arg1, callback) {
        //even more
        // arg1 now equals 'done'
        callback(null, 'done');
    }
], function (err, result) {
    //final callback function
    //finally do something when all function are done.
    // result now equals 'done'
});

So utilizing async.waterfall(), you would keep in touch with you code vertically as opposed to indenting it on a level plane and entering the pyramid of fate. Furthermore your code would be a great deal more sorted out and simple to peruse.

async.series()

Async gives another capacity to taking care of execution in arrangement, async.series(). Async arrangement works correspondingly to Async waterfall, by executing capacities in the exhibit in a steady progression with the distinction that it won’t pass the information starting with one capacity then onto the next, rather when every one of the capacities have finished their execution the aftereffect of the capacities will be accessible in the last callback as a cluster. Like async.waterfall() in async.series() too, when any of the capacities is called with a blunder callback, no more capacities in the exhibit are executed and the last callback is instantly called with the estimation of the mistake. Observe beneath for an unmistakable picture.

Async Series

var async = require('async');
async.series([
    function(callback){
        // do some stuff ...
        callback(null, 'one');
        /**
            The 1st parameter passed in callback.
            @null or @undefined or @false control moves to the next function
            in the array
            if @true or @string the control is immedeatly moved
            to the final callback fucntion with the value of err same as
            passed over here and
            rest of the functions in the array
            would not be executed
        */
    },
    function(callback){
        // do some more stuff ...
        callback(null, 'two');
    }
],
// optional callback
function(err, results){
    // results is now equal to ['one', 'two']
});

There are more cool capacities availaible with the async module ensure you look at them and use it in your Node.js ventures.

Visit the Github page here: Async.js Github

Managing callbacks using promises

Promises are alternative to callbacks, while dealing with asynchronous code. Promises return the value of the result or an error exception. The core of the promises is the .then() function, which waits for the promise object to be returned. The .then() function takes two optional functions as arguments and depending on the state of the promise only one will ever be called. The first function is called when the promise if fulfilled (A successful result). The second function is called when the promise is rejected.
Let us see the structure of a typical promise.

Promises

var outputPromise = getInputPromise().then(function (input) {
    //handle success
}, function (error) {
    //handle error
});
Chaining promises

We can also chain promises, this is an equivalent alternative to nesting callbacks. There are two ways to chaining promises. Promises can be chained inside or outside the handlers (.then() function). Chaining promises outside the handle is much clean and easy to read but we may have to chain promises inside the handler if we want to use some parameter in our next handler which available in the scope of the previous handler. Although too much chaining inside the handler will again result in horizontal code which we are trying to avoid. We can also chain promises with a combination of inside and outside chains. As a general rule, I would recommend you to avoid chaining inside the handler.

Chaining Promises

return getUsername().then(function (username) {
    return getUser(username);
})
.then(function (user) { //example of chaining outside the handler
   return userPassword().then(function(password){
        /**
            example of chaining inside the handler,
            since we need to use the @user param
            from the previous handler scope
        */
        if(user.password !== password){
            //reject promise or throw error
            return;
        }
    });
});
Changing callbacks to promises

There are a few good libraries for promises, we will look at the examples from the popular Kriskowal’s q promises library. Make sure you have q installed.

npm install --save q

callbacks to promises

var fs = require('fs');
var Q = require('q');
var readFile = Q.nfbind(fs.readFile);
readFile("foo.txt", "utf-8").then(function (text) {
   //handle success
}, function(err){
   //handle error
});
Writing functions that work with promises

We can also write our own function that works with promises. Look at the example below.

functions that work with promises

var fs = require('fs');
var Q = require('q');
//creation
function read(file) {
    var deferred = Q.defer();
    fs.readFile(file, 'UTF-8', function(err, data){
        if(err) {
            deferred.reject(err); //faliure, control will return to the second function
        } else {
            deferred.resolve(data) // fulfills the promise and returns data as value
        }
    });
        return deferred.promise // our mthod returns promise
}
//usage
read('demo.txt').then(function(data){
    //promise resolved with data
}, function(err){
    //promise rejected with err
});

There is more to promises, headover to the Github page to find out more.

Conclusion

Hence, we have seen, how we can manage the issue of callback hellfire in Node.js. There are a couple of more approaches to take care of the issue like utilizing generators, modularization and so on. In any case, we feel that async and promises are the two true answers for managing callback hellfire, with async favored more over guarantees. Be that as it may, recollect what we examined before, your center issue may very well not be the callback damnation, it could be ineffectively composed capacities. Ensure you keep your capacities thin and concentrated on a solitary undertaking. You will take in more about async and guarantees just when you will utilize them. So ensure you give them a shot next time in your undertakings.

Suggest

Memory Leaks and Profiling to Tune Node.js Apps

JavaScript with ReactJS and Nodejs

Node.js Tutorials: The Web Developer Bootcamp

Angular 2 and NodeJS - The Practical Guide to MEAN Stack 2.0

Complete Node JS Developer Course Building 5 Real World Apps