Understand Callback functions in JavaScript through examples9 min read

In modern JavaScript development, the concepts of functional programming and asynchronous programming become more and more prevalent. When talking about those terms, we usually see something like “callback” or “higher-order function”, for example, the .filter() method accepts a “callback” function and creates a new array for those elements that pass a certain condition in the callback function. So what is a callback function and how it can be used, let’s find out.

What is a callback function?

Simply put, a callback function is a function that passed as an argument of another function. Later on, it will be involved inside the outer function to complete some kind of action.

A higher-order function is a function that takes a function as its argument, or returns a function as a result.

In JavaScript, functions are first-class objects which means functions like any other objects can be passed as an argument of another function. Those functions are not directly executed but rather being executed in latter appropriate time, hence its name “callback” function.

Why we need callback functions?

OK, I have a basic understanding of the callback concept, so why should I pay my attention to this concept why do I need it? JavaScript is an event-driven language which means the flow of the program is determined by events such as a mouse click, reloading the page, etc… When JavaScript code has some events involved, instead of normally line-by-line, top-to-bottom execution, and waiting for a line to execute, it just skips the line cannot be executed right away, executes the next lines and moves back in an appropriate time. For better visualization, let’s write our first example:

document.addEventListener(‘click’,doSomething);

In this example, we use addEventListiner method to listen to the click event which we pass as a first argument, and the second argument is a callback function named doSomething. So, presume this code is on line 40, and when JavaScript encounters this line, the doSomething function isn’t executed right away. But instead, JavaScript will move to the next lines and the doSomething function only being executed when there is a click event.

When we want to call a function in JavaScript, we just simply write down the function name followed by trailing parentheses (e.g myFunction()). Notice with a callback function, we just write a function name without a pair of parenthesis afterward. If we put the pair of parentheses after a callback function, then the function will be executed immediately.

This is just a gentle prelude, let’s move on to see how to create a callback function and apply callback functions in different scenarios.

Callback functions in synchronous code

In synchronous JavaScript code, a callback function can be typically constructed as follow:

function callbackFunction(x) { return x; };

function myFunction(var1) {
    // some code
}

myFunction(callbackFunction);

First off, let’s create a simple function named addition which takes 2 operands and one callback function:

function addition(a, b, callback) {
    let result = callback(a, b);
    console.log("The result is: " + result);
}

Then we will define a callback function called callback which we later be call in the addition function:

function callback(a, b) {
    return a + b;
}

Finally we call the addition function:

addition(5, 8, callback);

The result of this function call will be: The result is: 13

Let’s break down what’s happening:

  • We initially create a function addition which takes 3 arguments, 2 numbers, and one callback function.
  • Then we create a callback function to add two numbers
  • Later on, we call the addition function, pass in 2 arguments, and one callback function as the last argument.

When the addition function is called, the callback function is not executed right away. JavaScript goes line-by-line in the addition function with our passed values. In the second line, it sees we call callback(a, b) and then at this moment, the callback function is executed with two arguments 5, 8 we passed in the addition function then it gives us the result of 13, later on, we display this result to the console.

This example might be trivial because we just have to simply add two numbers together. But it’s a great way to demonstrate the concept of callbacks with simplicity.

Typically, we can create a callback function as an anonymous function, i.g a function without a name, this code will work exactly the same as the code above:

function addition(a, b, callback) {
    let result = callback(a, b);
    console.log("The result is: " + result);
}

addition(5, 8, function(a, b) { return a + b });

Because we don’t call the callback function right away and just put its name in the as an argument of another function, here is how we can make sure our callback is a function, this code generally works as the code above:

function add(a, b, callback) {
    console.log(`The sum of ${a} and ${b} is ${a+b}.`);
    if (typeof callback === 'function') {
        callback();
    }
}
function displayCallback() {
    console.log('This must be printed after addition');
}
add(5, 6, displayCallback); 
/* Output:
The sum of 5 and 6 is 11.
This must be printed after addition
*/

Callback functions in asynchronous code

Typically, callback functions are more useful when it comes in terms of asynchronous code. Let’s first examine an example below:

Callbacks with setTimeout function

function first() {
    setTimeout(function() {
        console.log('first');
    }, 1000);
}

function second() {
    console.log('second');
}
first();
second();

Will it display ‘first’ to the console and then ‘second’? Here in the first function, we call the setTimeout function, passing to it 2 arguments, the first argument is the callback function to be executed, the second argument is the time in milliseconds to wait before the callback function can be executed. So in this example, the anonymous callback function we define inside the setTimeout method has to wait at least 1 second in order to run.

While waiting for the function to complete its execution, JavaScript doesn’t stop there and it will move on the next lines, hereby it sees we call the second function, then it immediately executes this function, and later on it moves back to the first function when the time in the setTimeout function has elapsed. Which gives us a result of:

second
first

Because of callback functions just like any other functions, that’s why we use the syntax of arrow functions for them:

setTimeout(() => {
    console.log('Something will be displayed after 2 seconds')
}, 2000);

Also read:

Callbacks make sure functions executed in a certain order

Here is an obvious example of using callbacks. A person wants to update his or her profile on a website, so first his/her needs to log in then update his/her profile, he or she can’t update profile without login:

logIn(client, function(someData) {
        // another inline callback function ...

        updateProfile(client, function(moreData) {
            // one more inline callback function ...
        });
});

We have a logIn function which takes client as the first argument, and another anonymous callback function as the second argument, inside this callback function we have another nested function named updateProfile.

The updateProfile function only executes after the first callback function runs. But be careful with those callbacks, if there are too many nested callbacks inside each other, people call them as “callback hell” and read the code is like a torture.

In this case above, callbacks are a way to make sure a function will not execute right away until another code has finished its execution.

Callback functions with events

Callbacks are also be used lavishly with events in JavaScript. For example we have a simple jQuery code, the callback function only executes when a button is clicked:

$("#btn").click(function() {
  alert("A button clicked!");
});

In the code above, we select the element with id btn which is a button, and then we have a click method which accepts a callback function, this callback function is the anonymous function and only executed when the button is clicked.

Callbacks with Promises

The most prominent example of callbacks perhaps with promises, the new feature which is added to JavaScript ES6 for handling asynchronous code. Basically, a promise is an object representing the eventual completion or failure of an asynchronous operation. Most of the time we are not interested in how to create promises, but rather how to consume them.

We are not going to talk much about Promises in this article. We just have to know after a promise is rather completed or finished, there is a method named .then which accepts a callback that we can use to handle events afterward.

For example, we need to put our clothes on the washing machine, if we provide soap and water, then our clothes will be cleaned.  If the clothes are cleaned, then we’ll want to put them in the dryer. After the dryer runs, if the clothes are dry, then we can fold them and put them away:

cleanClothes()
    .then(dryClothes)
    .then(putThemAway);

We don’t need to worry about the then method for now. As we mentioned in the previous sections, callbacks are a way to preserve a certain order of function calls. The dryClothes function only executes after the cleanClothes, the putThemAway functions only executes after the dryClothes function.

Callbacks with Functional Programming

If you’re familiar with .map, .filter, .reduce, or .forEach methods in JavaScript, those methods accept a callback function as a first argument. Let’s have a glance at the .map method:

var arr = [1, 3, 5];

function double(x) {
    return x * 2;
}
var anotherArr = arr.map(double);
console.log(anotherArr); // [ 2, 6, 10 ]

The map method is used with arrays, as can be seen in the code segment, we apply the map method in the arr, it takes a callback function named double. This function double will be involved for every element in the array arr and doubles each element. Then the result we store in anotherArr variable.

Also read:

We can also write the callback function by combining arrow function and anonymous function syntax as well:

var anotherArr = arr.map((x) => x * 2);
console.log(anotherArr); // [ 2, 6, 10 ]

Summary

JavaScript functions are first-class objects. Functions passed as arguments of other functions and are executed later, hence the name “callback”. A callback function can be used in different scenarios, but mostly they are used in the asynchronous code. JavaScript is an event-driven language, the flow execution lies on events such as users’ actions, those events typically attached with a callback. Callbacks are a great way to preserve a certain order of function executions, which means there is a chain of callbacks, the function A need executing first then B, then C…A callback function is just like other functions, it can be written as an anonymous function, and using the arrow function syntax. Some functional methods such as map, filter, reduce accepts a callback as the first argument to perform operations.

Previous Article
Next Article

Subscribe to my newsletter to get weekly updates

Categories

Archives