Understanding Callback Functions - for beginners [JavaScript]

Understanding Callback Functions - for beginners [JavaScript]

Introduction:

I was confused at first about what callback functions mean so I tried a few simple examples to understand what it does. In this article we will try to see with the same examples what callback functions are and how are they written.

Definition:

Here's what MDN says:

A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.

What does the definition mean let's take a simple example to understand:

const cb = () => console.log("Hello") //cb is defined here.
const strLength = (name, callbackFn) => callbackFn() // callbackFn can hold any callback function, we have not defined it. We are calling the function passed, inside.
strLength("Hello",cb) // cb is passed into another function strLength as an argument.

Let's see it diagrammatically:

A diagram showing how callback functions are written with ES6+ syntax

A diagram showing how callback functions are written without ES6+ syntax

const cb = () => console.log("Hello") //cb is defined here.
const strLength = (name, callbackFn) => callbackFn() // callbackFn can hold any callback function, we have not defined it. We are calling the function passed inside.
strLength("Hello",cb) // cb is passed into another function strLength as an argument.

So we understand here that, in the definition of strLength we have passed callbackFn as a parameter and callbackFn is just passed as any other variable with just its name.

What callbackFn function does, is not defined inside the strLength function definition. And callbackFn is invoked whenever strLength function is invoked.

Examples:

Now let's look at some simple examples:

Suppose with each name I want to do certain things with the name. So I can define a function that takes the name and inside that function, I can call another function that processes the name.

const cb = (namePassed) => console.log("The name is "+namePassed)
const cb2 = (namePassed) => console.log("The length is "+namePassed.length)

const strResults = (name,callbackFn) => callbackFn(name);

strResults("Excalidraw",cb); // The name is Excalidraw
strResults("TwitterExcalidraw",cb2); // The length is 17

Now here we can pass any name and it prints the output processed by cb & cb2 functions passed to it. cb and cb2 are invoked as defined in strResults function.

Now how is this helpful?

Imagine if whatever is written inside the cb2 function is taking time to execute and we wrote instructions inside cb directly inside strLength function? Like this:

const cb = (namePassed) => console.log("The name is "+namePassed)
const cb80 = (namePassed) => console.log("Hello and welcome to the team:  "+namePassed)

const strResults = (name,callbackFn) => callbackFn(name);
const strResults2 = (namePassed) => console.log("The length is "+namePassed.length) // What if this took more time? 

strResults("Excalidraw",cb); // The name is Excalidraw
strResults2("TwitterExcalidraw"); // The length is 17
strResults("Sundar",cb80); // Hello and welcome to the team:  Sundar

In that case, since JavaScript is a synchronous language, it can block the execution of the program, till the instructions specified are executed. Till the namePassed.length is not calculated it will not move ahead!

But what if we don't want it to block the execution?

Then we can define the instructions that are taking time inside another function and call it separately - a callback function. And we can tell JavaScript also with certain keywords that this callback function is going to take time to execute.

So as of now just know callback functions are useful in asynchronous operations.

Here's another thing, in the above example we have defined output for the callback function. What if we want to change the output of the callback function to something else? Say now instead of the name, we want to add a greeting message for the birthday.

// const cb = (namePassed) => { 
// console.log("The name is"+namePassed+" the length of name is: // "+namePassed.length);

const cb = (namePassed) => console.log("Hello and Happy Birthday: " + namePassed);

const strWish = (name,cb) => cb(name);
strWish("Sundar",cb);

There is no way we can do that without actually going back to where the callback function was defined and then changing it there. This can look cumbersome, isn't it?

To take care of it, we can define the callback function inside the outer function call itself as argument.

Examples below:

const strProcess = (name, cb) => cb(name) 
// cb is not defined, it just takes name as a parameter. 
// cb can do anything with the name passed to it.  

// Since cb is not defined, 
// we can print the name, length or any message. 
// We are free to do anything with the help of 
// dynamically defining what the function will do in function call

strProcess("Excalidraw",(nameGiven)=>console.log(nameGiven)) // Excalidraw

strProcess("Excalidraw",(nameGiven)=>console.log(nameGiven.length)) // 10 

strProcess("Excalidraw",(nameGiven)=>console.log(nameGiven+ " is awesome." ))
// Excalidraw is awesome

In the above example, I have not specified inside the outer function strProcess what cb will do.

cb can do anything we want to do with the parameter the name passed to it.

We have passed different functions and seen how with the same input it is giving output as we have defined in the arguments.

// Examples of setTimeout where we are defining function inside the function call itself. 

setTimeout(()=> console.log("hello"),3000)
setTimeout(()=> console.log("hello2"),4000)

The same outer function can be called to do different operations using different callback functions.

This can help in async operations, where we would want only a set of things to be performed when a particular function is called, not before it.

Anywhere outside the function, we do not wish to have this asynchronous operation be accessed.

The scope of that asynchronous callback function defined in the argument should be limited to that outer function call.

Conclusion:

So with the above, we got a glimpse of what callback functions are and how are they called. Callback functions are more utilized in asynchronous operations, understanding them would help us understand the code for asynchronous operations better.


Read more:

Callback Function | MDN

Callbacks | Javascript.info

Promise | Javascript.info

SetTimeout | MDN

Synchronous and Asynchronous Javascript | MDN