The simple guide to understanding Callback functions in JavaScript
Tags: javascript, webdev, beginners, programming
Published: 2022-09-23 10:07:34

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 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, 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 function`setTimeout(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 execute`callback` 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](https://twitter.com/emmanuelfkumah)