What is callback hell in Node.js ?

To know what is callback hell, we have to start from Synchronous and Asynchronous Javascript.

What is Synchronous Javascript?
In Synchronous Javascript, when we run the code, the result is returned as soon as the browser can do. Only one operation can happen at a time because it is single-threaded. So, all other processes are put on hold while an operation is executing.

What is Asynchronous Javascript?

  • Some functions in Javascript requires AJAX because the response from some functions are not immediate. It takes some time and the next operation cannot start immediately. It has to wait for the function to finish in the background. In such cases, the callbacks need to be asynchronous.
  • There are some external Javascript Web APIs that allows AJAX – Asynchronous Javascript and XML.
    In AJAX, many operations can be performed simultaneously.

What is a callback?

  • Callbacks are nothing but functions that take some time to produce a result.
  • Usually these async callbacks (async short for asynchronous) are used for accessing values from databases, downloading images, reading files etc.
  • As these take time to finish, we can neither proceed to next line because it might throw an error saying unavailable nor we can pause our program.
  • So we need to store the result and call back when it is complete.

What is callback hell?
This is a big issue caused by coding with complex nested callbacks. Here, each and every callback takes an argument that is a result of the previous callbacks. In this manner, The code structure looks like a pyramid, making it difficult to read and maintain. Also, if there is an error in one function, then all other functions get affected.



Example: This is the example of typical callback hell.

filter_none

edit
close

play_arrow

link
brightness_4
code

app.get("/details", function (req, res) {
    var name = req.query.name;
    console.log(name);
  
    Scopus.find({ name: name },
        { '_id': 0, 'authorId': 1 },
        function (err, result) {
            if (err) { }
            else {
                var searchResult = result[0]["authorId"];
                console.log(searchResult);
                var options = {
                    url: "https://api.elsevier.com/content/author/author_id/"
                        + searchResult + "?apiKey",
                    headers: { 'Accept': 'application/json' }
                };
                request(options, function (error, response, body) {
                    if (error) {
  
                        // Print the error if one occurred
                        console.error('error in Authors :', error);
  
                        // Print the response status code if a response was received
                        console.log('statusCode:', response && response.statusCode);
                        res.send("error")
                    }
                    else if (!error) {
                        var jsonObj = JSON.parse(body);
                        if (jsonObj['author-retrieval-response'] == undefined) {
                            res.send("No details");
                        }
                        else {
                            var reqData = jsonObj['author-retrieval-response'][0];
                            var authprofile = reqData["author-profile"]
                            var names = authprofile["preferred-name"]["indexed-name"]
                            console.log(names);
                            var citation = reqData["coredata"]["citation-count"];
                            var query = { authorId: searchResult };
  
                            Scopus.findOneAndUpdate(query, {
                                name: names,
                                citationCount: citation
                            }, function (err, doc, res) {
                                if (err) {
                                    console.log("error");
                                }
                                else {
                                    console.log("success");
                                }
                            })
                            res.render("details", { data: reqData });
                        }
                    }
                });
            }
        })
});

chevron_right


You can notice the nested callbacks is look like a pyramid that makes it difficult to understand.

How to escape from a callback hell?

  • JavaScript provides an easy way of escaping from a callback hell. This is done by event queue and promises.
  • A promise is a returned object from any asynchronous function, to which callback methods can be added based on the previous function’s result.
  • Promises use .then() method to call async callbacks. We can chain as many callbacks as we want and the order is also strictly maintained.
  • Promises use .fetch() method to fetch an object from the network. It also uses .catch() method to catch any exception when any block fails.
  • So these promises are put in event queue so that they don’t block subsequent JS code. Also once the results are returned, the event queue finishes its operations.
  • There are also other helpful keywords and methods like async, wait, settimeout() to simplify and make better use of callbacks.



My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.