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.
Table of Contents
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.