JavaScript Callback Functions

Taha Jiruwala
Level Up Coding
Published in
4 min readDec 3, 2022

--

Despite not knowing what a callback function is, chances are that you already use it without even realizing it. Methods like .map(), .filter(), and.reduce() all make use of callback functions.

Although callbacks can be confusing, you still need to learn them thoroughly as they form a critical concept in JavaScript. You can’t get very far without knowing callbacks.

In this article, you’ll learn what callbacks are, why they’re important, and how to use them.

What is a callback function?

In Javascript, every function is an object. This allows us to use a function as a parameter in another function which is the fundamental idea of callback functions.

A callback function is a function that is passed as an argument to another function and is expected to be called back at a later point in time.

The function that accepts other functions as arguments is called a higher-order function.

To illustrate how callback works, let’s look at a simple example.

function createGreeting(name, callback){ 
const greeting = `Hello ${name}!!`;
callback(greeting);
}

function logGreeting(greeting){
console.log(greeting);
}

createGreeting("James", logGreeting);
// Hello James!!

In this example, createGreeting is the higher-order function, which accepts two arguments, the name and the callback function. We use logGreeting as our callback function. When we execute the createGreeting function, notice that we are not appending parentheses to logGreeting when we pass it in as an argument. This is because we do not want to execute our callback function right away, we simply want to pass the function definition along to the higher-order function so that it can be executed later.

Why use Callback functions?

Callbacks are used in two different ways — in synchronous functions and asynchronous functions.

Callbacks in synchronous functions

If your function is executing from top-bottom, and waiting for the previous line to get executed before going to the next line, then your function is synchronous.

The purpose of using callbacks in synchronous code is that we can easily swap one part of the code with other.

Look at the example below, we can easily reuse the filter function to obtain even and odd numbers simply by swapping the callback function.

const numbers = [3, 4, 10, 20]
const getOddNums = num => num % 2 !== 0
const getEvenNums = num => num % 2 === 0

// Passing getOddNums function into filter
const oddNums = numbers.filter(getOddNums)

// Passing getEvenNums function into filter
const evenNums = numbers.filter(getEvenNums)

Now you know why we use callbacks in synchronous functions, let's move on to look at why we use callbacks in asynchronous functions.

Callbacks in asynchronous functions

Asynchronous means, if JavaScript needs to wait for something to complete, it will start finishing the other tasks while waiting for it.

A very common example of asynchronous function is setTimeout. It takes a callback function to execute it at a later time.

// Calls the callback after 1 second
setTimeout(callback, 1000)

Let’s see how setTimeout works if you give JavaScript other tasks to complete as well:

const fiveSecondsLater = () => console.log('5 seconds passed!')

setTimeout(fiveSecondsLater, 5000)
console.log('Start!')
console.log('Doing other tasks')

In the code above, JavaScript executes setTimeout. Then, it waits for five seconds and logs “5 seconds passed!”.

Meanwhile, while waiting for setTimeout to complete its 5 seconds, JavaScript continues to execute console.log("Start!") and console.log("Doing other tasks") .

So, this is what you’ll see as output for the above code:

// Start! (almost immediately)
// Doing other tasks (almost immediately after the first log)
// 5seconds passed! (after five seconds)

So with callbacks in asynchronous functions, we give the instruction in advance on what to do when tasks finishes.

Some common use cases of callbacks which tells JavaScript what to do-

  1. When an event fires (for example, keyboard key have been pressed).
  2. Complete an API call.
  3. After reading/writing to a file.

Hopefully this makes it clear why callbacks are useful and how you can use them.

Before we wrap up, lets look at a common problem that people usually run into while using callbacks.

Callback Hell

Callback Hell is a phenomenon where multiple callbacks are nested one after another. Every callback depends/waits for the previous callback to complete, thereby making a pyramid structure that is difficult to read as well as maintain.

Let’s look at an example of callback hell.

read('first.js', function(error, script) {
if (error) {
handleError(error);
} else {
// ...
read('second.js', function(error, script) {
if (error) {
handleError(error);
} else {
// ...
read('third.js', function(error, script) {
if (error) {
handleError(error);
} else {
// ....
}
});

}
});
}
});

It seems hard to follow the code, isnt’t it?

One solution to avoid callback hell is to divide code into smaller functions to avoid the nested structure.

read('first.js', step1);

function step1(error, script) {
if (error) {
handleError(error);
} else {
// ...
read('second.js', step2);
}
}

function step2(error, script) {
if (error) {
handleError(error);
} else {
// ...
read('third.js', step3);
}
}

function step3(error, script) {
if (error) {
handleError(error);
} else {
// ....
}
}

This seems more readable than the previous one, right?

The are other ways to tackle callback hell like promises and async/await. We will try to look at them in some future articles.

Wrapping up

Today, you learned what callbacks are, why they’re so important in JavaScript and how to use them. You also learned about callback hell and a way to address it. Hopefully, callbacks are no longer confusing to you now.

Do you still have any questions about callbacks? Feel free to leave a comment down below if you do and I’ll get back to you as soon as I can.

If you enjoyed this, please check out my other works as well.

--

--