top

An In-depth Understanding of Callback Functions in JavaScript

In the previous article, we went around a couple of issues that are faced by a newbie JavaScript programmer. We also came across what is called a callback function while fetching the information using an AJAX request. Let us try to understand a callback function in depth and how to go about using the same in our JavaScript code.What is a callback function?There is no standard definition of what a callback function is. But, callback functions are very common in asynchronous programming. We can get to know the JavaScript callback function defined as follows.A callback function is a function which is:passed as an argument to another functionis invoked inside the outer function to which it is passed as an argument to complete some action, routine or event.The outer function in the above reference, to which the callback function is passed as an argument, is a function which is actually undertaking the async task or delegating the same to another nested async function. Let us see in the examples below for both callback functions and the main function.Let us go through 2 JavaScript callback functions examples to understand the above definition. A Network Call based exampleA Network Call based examplefunction getUserName(callback){     var name;     $.get('https://randomuser.me/api/',  function(data) {             name = data.results[0].name.first                     + " " + data.results[0].name.last;             callback(name);     }); } var username ; function callback(res){     username = res;     document.write("Name: " + username); } getUserName(callback);In the above example, we are fetching the user details using an AJAX call and then using the callback function to show the name of the user. The callback function is responsible for using the fetched data and then updating the same in the DOM. The data is being fetched in the main function getUserName A DOM-based examplevar count = 0; //callback 1 function updateCount(){     $("#count").html(count); } //callback 2 function incCount() {     count++;     updateCount(); } //callback 3 function resetCount(){     count=0;     updateCount(); } $(document).ready(updateCount); $("#inc").click(incCount); $("#reset").click(resetCount); In the example, we are using 3 different callbacks, namely updateCount, incCount and resetCount for different events that are occurring on the page. As soon as the page loads, the count is updated on the page. Then, every time you click on the “Increment Me” button, it increments the counter in the code and also updates the same on the page. Similarly, the  “Reset Me” button resets the counter. The main functions here are the jQuery event handlers functions like ready and click, which operate on the DOM elements.Practice: Can you try and implement a decrement button with the above example?Like the above examples, a callback function would be necessary when you are dealing with any of the items on the following list:DOM event handling and User Input/Output.Databases (e.g. MySQL, PostgreSQL, MongoDB, Redis, CouchDB)APIs (e.g. Facebook, Twitter, Push Notifications)HTTP/WebSocket connectionsFiles (image resizer, video editor, internet radio)Why JavaScript Callback Functions?JavaScript code executes on a single thread and that too asynchronously. It means that the code execution stack doesn’t wait on any I/O operation, which happens outside the JavaScript thread, for example, a file read operation. Callback functions are a means to drive the execution of the code once the I/O operation is completed.This helps in a good way as it does not block the single threaded JavaScript code execution while waiting over any I/O operation and can execute the other functions in the stack. This is important in case of browsers, where the entire user experience is managed by the JavaScript rich applications since the inception of JavaScript.The way callbacks are managed internally is using an event loop. We shall talk about the JavaScript call stack and event loop in the articles to come.The Pyramid of DOOM or Callback Hell Imagine the following sequence of actions that are to be performed in JavaScript.Fetch User Information with an idUpdate the user Information on the pageFetch all the posts for the user with an idUpdate the first post on the pageFetch all the comments that are for the first post for the user with the mentioned idUpdate the comments on the pageI have tried to implement the above using a demo API called JSON PlaceHolder for implementing the code. You can checkout the link to see the documentation of the same.Let’s try and look at the code of the implementation of the above sequence of actions.function updateUserInfo(data){     var out = "";     for(var key in data){         if ( key!== 'id' && typeof data[key] !== "object") {             out += "<div>"+                 key + ": " + data[key] +                 "</div><br>";         }     }     $("#user").html(out); } function updateUserPosts(data){     var out = "";     //pulling out just the first post     for(var key in data[0]){         if ( key!== 'id' && typeof data[0][key] !== "object") {             out += "<div>"+                 key + ": " + data[0][key] +                 "</div><br>";         }     }     $("#post").html(out); } function updatePostComments(data){     var out = "";     //pulling out just the first post     for(var i=0; i<data.length; i++) {         for(var key in data[i]){             if ( key!== 'id' && typeof data[i][key] !== "object") {                 out += "<div>"+                     key + ": " + data[i][key] +                     "</div><br>";             }         }         out+="<hr/>"     }     $("#comments").html(out); } function fetchUserInfo(userId, callback, err){     $.ajax("https://jsonplaceholder.typicode.com/users/"+userId, {         success: function(data) {             setTimeout(function(){                 callback(data)             },1000);         },         error: err     }); } function fetchUserPosts(userId, callback, err) {     $.ajax("https://jsonplaceholder.typicode.com/posts/?userId="+userId, {         success: function(data) {             setTimeout(function(){                 callback(data)             },1000);         },         error: err     }); } function fetchPostComments(postId, callback, err) {     $.ajax("https://jsonplaceholder.typicode.com/posts/"+postId+"/comments", {         success: function(data) {             setTimeout(function(){                 callback(data)             },1000);         },         error: err     }); } function errCallback(data){     console.log("Error:" + data.status); } // Callback Hell fetchUserInfo("2", function(user){     // console.log(data);     updateUserInfo(user);         fetchUserPosts(user.id, function(posts){         updateUserPosts(posts);                 fetchPostComments(posts[0].id, function(comments){             updatePostComments(comments)         }, errCallback);             }, errCallback);     }, errCallback); I have implemented each of the action as a separate function. For example, the function fetchUserInfo is only going to fetch the user information with a given id and use a callback to pass on the information to the parent function. The function updateUserInfo is only going to update a section of the page with the pass on information. Notice that fetch function are using a callback to pass on the information to the parent function and update functions do not. Along with that, we have a very generic error handler, which is the same for all the actions and prints just the request status. The last piece of code is the actual execution of the sequence.Now, let’s look at the issues with the above implementation.Success and Error CallbacksFor each action, where we are fetching the data, there is a success callback and then there is an error callback. Both scenarios are important to handle for each action, hence more functions.Error Handling and Fallbacks.In the example above, we are using a generic error handler. But real life actions are not so simple. Imagine the sequence of a checkout pipeline on an e-commerce website. What if you are about the pay for an item that is not available? You would want to go back a step and inform the user about the same, won’t you?. This would lead to a separate handler at each step for both success and failure scenario.Code ManagementThe example we are dealing with is a very simple one. Say, we are to also fetch the images that are associated with a user id along with user posts and also update the same before the posts. We will have to rewrite a sequence of code and also all the supporting function to support success and error scenarios. How about we add the albums and then their comments too? :)Sequential fetchingWe already know the user id, hence we should be able to fetch the user information and posts parallelly. But in our implementation, we first have to fetch the user information and then the user posts. There should be a cleaner way to deal with the same.Promises to the rescuePromises is a representation of a sync operation in the form of an object. It provides a more stateful approach to any async operation and tries to structure the implementation and its attachment with the callbacks. We shall talk about promises in more details in the next article.Let me hear your thoughts on this article. Any suggestions and word of improvements are also welcome.
Rated 4.5/5 based on 11 customer reviews
Normal Mode Dark Mode

An In-depth Understanding of Callback Functions in JavaScript

Chetan Agrawal
Blog
12th Sep, 2018
An In-depth Understanding of Callback Functions in JavaScript

In the previous article, we went around a couple of issues that are faced by a newbie JavaScript programmer. We also came across what is called a callback function while fetching the information using an AJAX request. Let us try to understand a callback function in depth and how to go about using the same in our JavaScript code.

What is a callback function?

There is no standard definition of what a callback function is. But, callback functions are very common in asynchronous programming. We can get to know the JavaScript callback function defined as follows.

A callback function is a function which is:

  • passed as an argument to another function
  • is invoked inside the outer function to which it is passed as an argument to complete some action, routine or event.

The outer function in the above reference, to which the callback function is passed as an argument, is a function which is actually undertaking the async task or delegating the same to another nested async function. Let us see in the examples below for both callback functions and the main function.

Let us go through 2 JavaScript callback functions examples to understand the above definition. A Network Call based example


A Network Call based example

function getUserName(callback){
    var name;
    $.get('https://randomuser.me/api/',  function(data) {
            name = data.results[0].name.first
                    + " " + data.results[0].name.last;
            callback(name);
    });
}

var username ;

function callback(res){
    username = res;
    document.write("Name: " + username);
}

getUserName(callback);

In the above example, we are fetching the user details using an AJAX call and then using the callback function to show the name of the user. The callback function is responsible for using the fetched data and then updating the same in the DOM. The data is being fetched in the main function getUserName 


A DOM-based example
var count = 0;

//callback 1
function updateCount(){
    $("#count").html(count);
}

//callback 2
function incCount() {
    count++;
    updateCount();
}

//callback 3
function resetCount(){
    count=0;
    updateCount();
}

$(document).ready(updateCount);
$("#inc").click(incCount);
$("#reset").click(resetCount);

In the example, we are using 3 different callbacks, namely updateCount, incCount and resetCount for different events that are occurring on the page. As soon as the page loads, the count is updated on the page. Then, every time you click on the “Increment Me” button, it increments the counter in the code and also updates the same on the page. Similarly, the  “Reset Me” button resets the counter. The main functions here are the jQuery event handlers functions like ready and click, which operate on the DOM elements.

Practice: Can you try and implement a decrement button with the above example?

Like the above examples, a callback function would be necessary when you are dealing with any of the items on the following list:

  • DOM event handling and User Input/Output.
  • Databases (e.g. MySQL, PostgreSQL, MongoDB, Redis, CouchDB)
  • APIs (e.g. Facebook, Twitter, Push Notifications)
  • HTTP/WebSocket connections
  • Files (image resizer, video editor, internet radio)


Why JavaScript Callback Functions?

JavaScript code executes on a single thread and that too asynchronously. It means that the code execution stack doesn’t wait on any I/O operation, which happens outside the JavaScript thread, for example, a file read operation. Callback functions are a means to drive the execution of the code once the I/O operation is completed.

This helps in a good way as it does not block the single threaded JavaScript code execution while waiting over any I/O operation and can execute the other functions in the stack. This is important in case of browsers, where the entire user experience is managed by the JavaScript rich applications since the inception of JavaScript.

JavaScript Callback Functions

The way callbacks are managed internally is using an event loop. We shall talk about the JavaScript call stack and event loop in the articles to come.

The Pyramid of DOOM or Callback Hell 

The Pyramid of DOOM or Callback Hell


Imagine the following sequence of actions that are to be performed in JavaScript.

  1. Fetch User Information with an id
  2. Update the user Information on the page
  3. Fetch all the posts for the user with an id
  4. Update the first post on the page
  5. Fetch all the comments that are for the first post for the user with the mentioned id
  6. Update the comments on the page

I have tried to implement the above using a demo API called JSON PlaceHolder for implementing the code. You can checkout the link to see the documentation of the same.

Let’s try and look at the code of the implementation of the above sequence of actions.

function updateUserInfo(data){
    var out = "";
    for(var key in data){
        if ( key!== 'id' && typeof data[key] !== "object") {
            out += "<div>"+
                key + ": " + data[key] +
                "</div><br>";
        }
    }
    $("#user").html(out);
}

function updateUserPosts(data){
    var out = "";
    //pulling out just the first post
    for(var key in data[0]){
        if ( key!== 'id' && typeof data[0][key] !== "object") {
            out += "<div>"+
                key + ": " + data[0][key] +
                "</div><br>";
        }
    }
    $("#post").html(out);
}

function updatePostComments(data){
    var out = "";
    //pulling out just the first post
    for(var i=0; i<data.length; i++) {
        for(var key in data[i]){
            if ( key!== 'id' && typeof data[i][key] !== "object") {
                out += "<div>"+
                    key + ": " + data[i][key] +
                    "</div><br>";
            }
        }
        out+="<hr/>"
    }
    $("#comments").html(out);
}

function fetchUserInfo(userId, callback, err){
    $.ajax("https://jsonplaceholder.typicode.com/users/"+userId, {
        success: function(data) {
            setTimeout(function(){
                callback(data)
            },1000);
        },
        error: err
    });
}

function fetchUserPosts(userId, callback, err) {
    $.ajax("https://jsonplaceholder.typicode.com/posts/?userId="+userId, {
        success: function(data) {
            setTimeout(function(){
                callback(data)
            },1000);
        },
        error: err
    });
}

function fetchPostComments(postId, callback, err) {
    $.ajax("https://jsonplaceholder.typicode.com/posts/"+postId+"/comments", {
        success: function(data) {
            setTimeout(function(){
                callback(data)
            },1000);
        },
        error: err
    });
}

function errCallback(data){
    console.log("Error:" + data.status);
}

// Callback Hell
fetchUserInfo("2", function(user){
    // console.log(data);
    updateUserInfo(user);
   
    fetchUserPosts(user.id, function(posts){
        updateUserPosts(posts);
       
        fetchPostComments(posts[0].id, function(comments){
            updatePostComments(comments)
        }, errCallback);
       
    }, errCallback);
   
}, errCallback);


I have implemented each of the action as a separate function. For example, the function fetchUserInfo is only going to fetch the user information with a given id and use a callback to pass on the information to the parent function. The function updateUserInfo is only going to update a section of the page with the pass on information. Notice that fetch function are using a callback to pass on the information to the parent function and update functions do not. Along with that, we have a very generic error handler, which is the same for all the actions and prints just the request status. The last piece of code is the actual execution of the sequence.

Now, let’s look at the issues with the above implementation.

  • Success and Error Callbacks

For each action, where we are fetching the data, there is a success callback and then there is an error callback. Both scenarios are important to handle for each action, hence more functions.

  • Error Handling and Fallbacks.

In the example above, we are using a generic error handler. But real life actions are not so simple. Imagine the sequence of a checkout pipeline on an e-commerce website. What if you are about the pay for an item that is not available? You would want to go back a step and inform the user about the same, won’t you?. This would lead to a separate handler at each step for both success and failure scenario.

  • Code Management

The example we are dealing with is a very simple one. Say, we are to also fetch the images that are associated with a user id along with user posts and also update the same before the posts. We will have to rewrite a sequence of code and also all the supporting function to support success and error scenarios. How about we add the albums and then their comments too? :)

  • Sequential fetching

We already know the user id, hence we should be able to fetch the user information and posts parallelly. But in our implementation, we first have to fetch the user information and then the user posts. There should be a cleaner way to deal with the same.


Promises to the rescue

Promises is a representation of a sync operation in the form of an object. It provides a more stateful approach to any async operation and tries to structure the implementation and its attachment with the callbacks. We shall talk about promises in more details in the next article.

Let me hear your thoughts on this article. Any suggestions and word of improvements are also welcome.

Chetan

Chetan Agrawal

Blog Author

Chetan Agrawal is a co-founder and product head at Sportzify, a startup in sports segment. Prior to this, he was working as a full stack developer at Amazon and CK-12 Foundation. He loves tinkering around with Javascript Frameworks, Express.js and React.js being his favorite.


Website : http://www.chetan1507.in/

Leave a Reply

Your email address will not be published. Required fields are marked *

Top comments

Marry

20 November 2018 at 4:26pm
Awesome article

SUBSCRIBE OUR BLOG

Follow Us On

Share on

other Blogs