The simple guide to understanding Callback functions in JavaScript

The simple guide to understanding Callback functions in JavaScript

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 function sayHi, and parameter 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 the argument, which is the actual data you want to pass to the function.The argument is the data inputted. Any data we input will be stored in the parameter 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 and secondFunc
  • We run the firstFunc function and pass the secondFunc 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 the firstFunc, the secondFunc 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 and multiplyNum which accepts two numbers, and perform an addition and multiplication on them respectively.
  • We run the operateNum function, and then pass either the addNum or multNum function as an argument to it.
  • We also pass the operateNum two numbers (3,and 4) as arguments..
  • With this approach, we will now run the addNum or multNum function only in the operateNum function.
  • The addNum or multNum function can accept the two numbers passed to the operateNum

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 the getUserInput function. The greetUser function is our callback function. It will be executed later, when the getUserInput function is executed.
  • We are passing the greetUser function as an argument to the getUserInput function because, the greetUser depends on a value from the getUserInput. Hence, we do not want to execute the greetUser function immediately.
  • When thegetUserInput() 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, the greetUser 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-in array.filter() method.
  • When the array.filter() method gets executed, we then run the isOdd() function.
  • Since it is synchronous, the console.log(oddNumbers) gets blocked, waiting for the numbers.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 of btn from the html, and assign it to the myBtn variable
  • We attached an addEventListener() function to the myBtn 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

#2Articles1Week Challenge