JavaScript

Javascript Callback Function Example

By now you are all aware that functions in Javascript are first-class objects, which means that they behave like any other object: they can be stored in variables, be passed as arguments, or be returned from other functions.

Because of this peculiar quality of theirs, they can be passed as arguments to other functions, which is the essence of callback functions. But let’s see it in more detail!

What are Callback Functions?

Callback Functions are a derivative of a programming paradigm known as functional programming. A callback function, in itself is just a function passed as an argument to another function, and then executed inside of it. This is a widespread solution to many problems, so now we call it “callback pattern”. This pattern is used a lot not only in plain Javascript, but also other programming languages and frameworks such as jQuery or Node.js.


 
Let’s see a very typical use in jQuery:

$("#btn_1").click(function() {
  alert("Btn 1 Clicked");
});

As you can see, what we have passed as an argument to the method click() is not a variable but a whole function. The method will then invoke the function we passed to it. Let’s see how this goes in Javascript. Take a look at the code snippet below:

var friends = ["John", "Jane", "Bill", "Mary"];
​
friends.forEach(function (eachName, index){
console.log(index + 1 + ". " + eachName); 
});

Note that in this example, as in the previous one, we have passed an anonymous function as a parameter to our method (forEach() in the second case). Let’s see how callback works with non-anonymous functions and start making our own callback functions.

When we are passing a callback function as a parameter, we are only passing it’s definition, we are not executing it, as would be the case if we also passed the trailing pair of () parenthesis. Since the containing function also has the callback’s definition, it can execute it anytime. Usually, the callback is not executed immediately, but is “called back” at a specified time inside the containing function’s body. That is also the reason why we call it “callback”.

Considering this, you now understand that in the example above, the unnamed function passed as a parameter is not executed immediately, but will be called later inside the method’s body. Even though the callback function doesn’t have a name, it can be accessed later using the argument object of the containing function.

Another important thing to point out is that callback functions are closures. That means that the callbacks are executed as if they were defined inside the body of the containing functions. That makes it possible for callbacks to access the variables set either in the containing function’s scope or in the global one, apart from those placed in it’s own scope.

Using named functions as callbacks

In the previous examples we only used anonymous functions as callbacks. Another very popular way to do this is by declaring a named function and then passing it as a callback by it to the containing function. Take a look at the code snippet below:

​var allUserData = [];
​
​function logStuff (userData) {
    if ( typeof userData === "string")
    {
        console.log(userData);
    }
    else if ( typeof userData === "object")
    {
        for (var item in userData) {
            console.log(item + ": " + userData[item]);
        }
​
    }
​
}
​
​function getInput (options, callback) {
    allUserData.push (options);
    callback (options);
​
}
​
getInput ({name:"John", speciality:"JavaScript"}, logStuff);

What we’ve done here is pretty simple. First we have declared an empty array named allUserData as a global variable. Then we have declared a simple function named logStuff which just puts out things in our console. After defining this function we create another one named getInput, which will serve us as a containing function, as it takes two parameters, the second of which is a callback function. What we do in the last line is put all this to work by invoking the containing function. Considering our code, it would only take the inputs we gave it and print it out in the console. Easy, right?

Passing parameters to callback functions

Since callback functions are in essence just that, functions, we can pass parameters to them. These can be global or local variables, or really, whatever. Take a look at the code snippet below:

var generalLastName = "Clinton";
​
​function getInput (options, callback) {
    allUserData.push (options);
    callback (generalLastName, options);
}

In this example the variable we have passed to the callback function is generalLastName, which is a global one, and also the local options.

Checking if callback is a function

When using named functions as callbacks, it is very recommended to check if it really is a function first, before passing it as an argument. This is because it may happen that the argument you pass is not a function, and you would be presented with a runtime error. Take a look below:

function getInput(options, callback) {
    allUserData.push(options);
​
    if (typeof callback === "function") {
        callback(options);
    }
}

This is how the previous example would look with this added benefit.

this in a callback function

When we use this keyword in a function that we will later use as a callback function we might encounter some problems. We have to preserve the value stored in it by modifying how we execute the callback function, or the keyword will point to the global window object when callback is passed to a global function, or to the object of the containing function. Let’s see the example below:

var clientData = {
    id: 01611,
    fullName: "Not Set",
    setUserName: function (firstName, lastName)  {
      this.fullName = firstName + " " + lastName;
    }
}
​
​function getUserInput(firstName, lastName, callback)  {
    callback (firstName, lastName);
}
getUserInput ("Doe", "John", clientData.setUserName);
​
console.log (clientData.fullName);
​
console.log (window.fullName); 

Firstly, we have declared and object with some properties and a method named setUserName, which uses the keyword this. The latter refers to the fullName property of the clientData object.

Secondly, we define the containing function, which takes three parameters, two of which are firstName and lastName, both properties of the object, and the third one is the callback function. After doing that we invoke the containing function, giving it clientData.setUserName as a callback function. Notice that it is the method we defined inside the object clientData.

When the callback function is executed, this.fullName will not set the fullName property in the clientData object, since the function getUserInput is a global object. Instead it will set it on the window object, because there is where this is currently pointing.

This problem can be easily solved by using the call and apply methods of functions. These methods are used to set the this object inside the function and to pass arguments to the functions. Both methods take the value to be used as the this object inside the function as a first parameter. While call takes the rest of the arguments individually (separated by commas), apply takes as a second argument an array of values that represent the rest of the arguments.

Let’s see below how would we be using apply in the previous example to fix our problem:

​function getUserInput(firstName, lastName, callback, callbackObj)  {
    callback.apply (callbackObj, [firstName, lastName]);
}

getUserInput ("John", "Doe", clientData.setUserName, clientData);
​
console.log (clientData.fullName); 

As we explained the very subtle difference in using the two functions, you can very easily figure out how to do this by using the call function.

“Callback Hell”

You might have heard about asynchronous code execution. It consists of code executing in any order, not only line by line. Sometimes, when using multiple callbacks on numerous levels of your code, you will have a very cluttered code which will be impossibly difficult to follow. To demonstrate how your code could look like, take a look at the code snippet below:

var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory});
p_client.open(function(err, p_client) {
    p_client.dropDatabase(function(err, done) {
        p_client.createCollection('test_custom_key', function(err, collection) {
            collection.insert({'a':1}, function(err, docs) {
                collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) {
                    cursor.toArray(function(err, items) {
                        test.assertEquals(1, items.length);
​
                        // Let's close the db​
                        p_client.close();
                    });
                });
            });
        });
    });
});

The code is difficult to understand because of how many callbacks there are. Even though you might not encounter this problem very often, you better be prepared for it if, on the off chance, you do (you might, if you look at the MongoDB driver for Node.js). This problem is called “callback hell” and the solution to it is to use named functions as callbacks instead of using anonymous ones that are defined in the parameter of the containing function. Another recommended practice is to divide your code into modules. Never underestimate the power of modularization.

Download the source code

This was an example of Callback Functions in Javascript. Download the source code for this tutorial:

Download
You can download the full source code of this example here : CallbackFunctions

Era Balliu

Era is a Telecommunications Engineering student, with a great passion for new technologies. Up until now she has been coding with HTML/CSS, Bootstrap and other front-end coding languages and frameworks, and her recent love is Angular JS.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Madog
Madog
7 years ago

Wow, you’re still a student?
You explain these concepts so well. It’s the first time I actually understood the finer details of callbacks.
I’m going to implement these concepts first thing tomorrow.
Thanks

Eric Walden
Eric Walden
7 years ago

How would I create unit tests and assertions for a callback function? I am having problems with this as part of my entrance exam

Back to top button