Functions are one of the underlying building blocks in JavaScript. It helps us perform a specific task when it is executed.
In this post, we take a look at callback functions with easy to follow explanation and examples.
By the end of the article, you should be able to understand callback functions, including both synchronous and asynchronous callback functions.
Let's get started.
Understanding functions in JavaScript
Before we dive into callback function, let's revisit function
in JavaScript
A
function
, is a block of code that performs a particular task when it is carried out.
For an operation to qualify as a function, it requires a certain input and return an output, and there should be a clear relationship between the input and the output.
To use a function, we must define and call it.
Take a look at the code below
// define the function
function sayHi(user){
console.log(`Hello ${user}`)
}
//callthe function
sayHi("Emmanuel")
- In the code above, we define the function using the
function
keyword, followed the name of the functionsayHi
, andparameter
to the function, enclosed in parentheses. - The statements that will be carried out will then be enclosed in the curly braces
{ }
- Run the function using the name of the function, followed by parenthesis
()
. - The
()
will contain theargument
, which is the actual data you want to pass to the function.Theargument
is the data inputted. Any data we input will be stored in theparameter
which serves as a placeholder. - Finally, in the body of the function, we run our task and output the required data to the user.
Function Arguments
Function arguments are the actual values passed to (and received) by the function.
The argument
can be of any data type (string
, objects
, arrays
, numbers
etc.). When we pass the required argument
to the function,it will be stored in the parameter
.
The parameter
serves as the placeholder to the actual value to be passed.We can then use the parameter
in the body of the function to perform some tasks.
JavaScript allows us to pass a function as an argument to another function and use that function in body of the outer function. This approach is what is termed callback
function.
Below, we take a dive to understand callback
function
What is a callback function ?
A callback function is a function that is passed as an
argument
to another function, which is then invoked inside the outer function to complete some kind of routine or action.
Let's see the code below:
function firstFunc(anotherFunc){
//execute the other function
anotherFunc()
}
// Define our callback function
function secondFunc(){
console.log("I am another function called inside the outer function" )
}
//execute the firstFunc and pass the second function as the argument
firstFunc(secondFunc)
We can also use arrow function , and declare the second function directly in the main function.
See the code below:
firstFunc(()=>{ console.log("I am another function called inside the outer function" )
})
The output:
"I am another function called inside the outer function"
- In the code above, we declared two functions
firstFunc
andsecondFunc
- We run the
firstFunc
function and pass thesecondFunc
function as an argument.
Note that, the actual data we are passing to the function is another function declared as secondFunc
Now, within the body of the firstFunc
we can run, the secondFunc
we declared.
- Because we are passing
secondFunc
as an argument to thefirstFunc
, thesecondFunc
becomes our callback function
If you've defined a function and you're not invoking it by yourself — but rather supply it as an argument to another function — then you've created a callback.
Passing a function to another function as argument !
Functions are considered as objects
in JavaScript. Like objects
, functions have properties and methods. We can assign an object to a variable, as well as pass objects as arguments to a function.
Because we can pass objects as arguments to a function, and a function is technically an object, we can also pass a function as an argument to another function. In the outer function, we callback the passed function. Using this approach, we extend the functionality of our application.
Example of a callback function.
Supposing we want to perform specific operations, for instance , add and multiply two numbers. We can simply declare the functions for these tasks as below:
//addNum function
function addNum(x,y){
return x +y
}
//multiplyNum function
function multNum(x,y){
return x * y;
}
// run the addNum and multNum function
console.log(addNum(3,4))
console.log(multNum(3,4));
The output for these operations will be
7
12
Now, supposing we want to extend this code, to output certain statement before performing the operations, how will we approach it?
We can declare a higher-order function (a function that accepts another function as a parameter, and return that function). In the body of the higher-order function, we include the** statement to be outputted**, and the required function to run.
Let's see how to achieve that:
- Define the higher-order function which will include the statement to be outputted
- Define the tasks (mathematical operations) to be performed in their respective functions
- Run the higher-order function and pass the actual function to be performed as an
argument
. - In the body of the higher-order function, run the actual function to perform the task
The code below will be:
//higher order function
function operateNum(callback, x, y){
console.log(`Let's learn some operations using ${x} and ${y}`)
// execute the callback function
console.log(callback(x,y))
}
//addNum function
function addNum(x,y){
return (`Adding ${x} and ${y} gives us: ${x + y}`)
}
//multiplyNum function
function multNum(x,y){
return (`Multiplying ${x} and ${y} gives us: ${x * y}`)
}
/* call the higher-order function and a pass it the addNum or multNum function
pass the actual numbers to perform the operation on.
*/
operateNum(addNum,3,4)
operateNum(multNum,3,4)
- In the code above, we define the
operateNum
as the higher-order function - We declared two other functions,
addNum
andmultiplyNum
which accepts two numbers, and perform an addition and multiplication on them respectively. - We run the
operateNum
function, and then pass either theaddNum
ormultNum
function as anargument
to it. - We also pass the
operateNum
two numbers (3,and 4) as arguments.. - With this approach, we will now run the
addNum
ormultNum
function only in theoperateNum
function. - The
addNum
ormultNum
function can accept the two numbers passed to theoperateNum
Because we are passing the addNum
or multNum
as functions to the operateNum
, the addNum
and multNum
are referred to as a callback
functions.
We will only "call" these functions later inside the operateNum
function.
What's important is that, the higher-order function takes the full responsibility of invoking the callback function later.
Why do we need a callback function ?
JavaScript runs code synchronously, this means every line of code runs line by line, from top to down. With this approach, a statement has to wait for the previous statement to execute before it can run.
However, there are instances where you want a function to run after something else has happened.
- For instance, when we request data from a server, it takes some time for the response to get to us.
- In such situations, we will have to wait for the response before we can move to the next line of code to execute.
- Since waiting for the response could take time, our entire application will grind to a halt whiles the data is being fetched from the server.
- This scenario can be avoided when we use
callback
functions. - The callback function ensures that a function will run later, right after a certain task has been completed
There are two types of callback functions, **synchronous **and **asynchronous **callback functions.
Synchronous Callback function.
The synchronous callback is executed **during **the execution of the higher-order function that uses the callback.
Let's understand the above using the example below :
function greetUser(name){
console.log(`Hello ${name} welcome!!`)
}
function getUserInput(callback){
const userInput = prompt("Please enter your name");
callback(userInput)
}
//execute the higher order function
getUserInput(greetUser)
console.log('waiting to run')
In the code above:
- We pass the
greetUser
function as an argument to thegetUserInput
function. ThegreetUser
function is ourcallback
function. It will be executed later, when thegetUserInput
function is executed. - We are passing the
greetUser
function as an argument to thegetUserInput
function because, thegreetUser
depends on a value from thegetUserInput
. Hence, we do not want to execute thegreetUser
function immediately. - When the
getUserInput()
is executed, it provides a prompt to enter an input. - The
greetUser
function relies on the inputed value, once that value is passed to it, thegreetUser
function will also be executed.
The above is an example of a synchronous callback, because the callback function get executed immediately the higher-order function is executed.
Synchronous callback functions are blocking, meaning any statement below the higher-order function will not be excuted, until the higher-order function gets executed. Run the code snippet above to verify
Examples of synchronous callback functions
Most JavaScript built-in methods especially the Array
methods utilizes synchronous callbacks. Any function passed to the desired method will be executed immediately the built-in method is run.
Example of built-in JavaScript methods are :
array.map(callback)
-
array.forEach(callback)
-
array.reduce(callback)
etc
Take another look at a synchronous callback function using the array.filter()
method
let numbers = [1, 2, 4, 7, 3, 5, 6];
const isOdd = (num)=>{
return num % 2
}
const oddNumbers = numbers.filter(isOdd);
console.log(oddNumbers)
The output of the code will be
[1, 7, 3, 5]
- The
isOdd
function is passed as an argument to the built-inarray.filter()
method. - When the
array.filter()
method gets executed, we then run theisOdd()
function. - Since it is synchronous, the
console.log(oddNumbers)
gets blocked, waiting for thenumbers.filter(isOdd)
to run.
Asynchronous Callback function
The asynchronous callback is executed *after * the execution of the higher-order function.
The higher-order function will run first, without waiting for the callback function. It however ensures to execute the callback eventually on a certain event, for instance, on a click of a button.
Asynchronous callbacks are non-blocking: if there's any code below the higher-order function, it doesn't wait untill the the callback function completes before that code runs.
Take a look at the code below:
console.log("start set timeout")
setTimeout(function callLater(){
console.log("I will run later")
}, 3000)
console.log('end set timeout ')
In the code above, callLater()
is an asynchronous callback function, because, the higher-order functionsetTimeout(callLater, 3000)
starts and completes its execution. However, callLater()
function is executed after 3 seconds.
Because aynchronous callback functions are non-blocking, the code below the setTimeout()
function which is console.log('end set timeout')
will still run whiles the callback function is in progress.
To verify, check the output of the code below:
"start set timeout"
"end set timeout "
"I will run later"
Example 2: Executing a callback function on event
JavaScript is an event-driven programming language, hence we can executecallback
functions when an event occurs, for instance when a user clicks on a button.
Let's take another look at an asynchronous callback function.
In the code below, we run the handleClick()
callback function only when the button
element has been clicked.
//index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<button id="btn">Click me</button>
</body>
</html>
//index.js
const myBtn = document.getElementById("btn")
myBtn.addEventListener('click', function handleClick(){
console.log('btn clicked')
})
console.log('non blocking so i will run')
In the code above
- We selected a
button
element with and id ofbtn
from the html, and assign it to themyBtn
variable - We attached an
addEventListener()
function to themyBtn
element - The
addEventListener()
is a higher-order function which listens for a "click" event. - We pass the
handleClick()
function as a callback function to the higher-order function.
When the code is executed, the addEventListener()
function will execute first, and then listen for click
on the button
element.
Eventually, on a click on the button, the handleClick()
callback function will be executed after the execution of the higher-order function.
Because asychronous callback functions are non blocking, the code below the addEventLister()
method will run whiles the handleClick()
function is waiting to execute.
The output of the code above is :
"non blocking so i will run"
"btn clicked"
Asynchronicity means that, if JavaScript has to wait for an operation to complete, it will execute the rest of the code while waiting.
Summary
Let's recap what we have learnt:
- A high-order function is a function that accepts another function as an argument.
- A callback function is a function passed into another function as an argument to be executed later
- A callback function ensures that a function will not run when a task is in progress (for instance, fetching data from an api), but will only run, right after the task has been completed
- There are two type of callback functions , synchronous and asynchronous callback functons
If you have found this article useful, give me thumps up. It motivates me to keep writing more. If you have any questions, or comment , feel free to let me know. Please do share this article on your social media channel, it might help someone becomes a better developer.
Let's connect on Twitter