A callback is a function that is passed as an argument to another function and is executed after the completion of an asynchronous operation. Callbacks are a common way to handle asynchronous operations in JavaScript. They allow you to define what should happen after an operation completes without blocking the main thread.
To explore callbacks, let’s consider a scenario where we need to fetch data from a server and process it. We will use a simple example to demonstrate how callbacks can be used to handle asynchronous operations.
In the following code snippet, we have three functions: getId, getCourses, and getGrades. Each function represents a step in the process of fetching data for a student. The getId function fetches the student’s ID from the database, the getCourses function fetches the student’s courses, and the getGrades function fetches the student’s grades.
The code above demonstrates a synchronous approach to fetching data for a student. A naive implementation might look like this:
However, in a real-world scenario, fetching data from a server is an asynchronous operation that takes time to complete. Let’s simulate this by adding a delay to the getId function:
If you run the code above, you will notice that the student_id is undefined. This is because the getId function returns id before the asynchronous operation completes. The setTimeout function schedules the assignment of id to “jdoe23” after 5000 milliseconds, but the getId function returns id immediately after the setTimeout function is called.
Let’s try another approach by returning the id value inside the setTimeout function:
If you run the code above, you will notice that the student_id remains undefined. This is because the return id statement inside the setTimeout function does not return the value to the getId function. The setTimeout function is executed asynchronously, and the return id statement is executed in a different context.
Now, let us use a callback function to handle the asynchronous operation:
In the code above, we modified the getId function to accept a callbackFunction as an argument. The callbackFunction is executed after the asynchronous operation completes. We pass the id value to the callbackFunction inside the setTimeout function. This allows us to handle the asynchronous operation in a non-blocking way.
When you run the code above, you will see the following output:
Let’s now apply the same approach to the getCourses function:
When you run the code above, you will see the following output:
Finally, let’s apply the same approach to the getGrades function:
When you run the code above, you will see the following output:
In this example, we used callbacks to handle asynchronous operations in a non-blocking way. We passed callback functions to the getId, getCourses, and getGrades functions to handle the results of the asynchronous operations. This allowed us to fetch data for a student in a sequential manner without blocking the main thread.
Callback Hell
One of the challenges of using callbacks for handling asynchronous operations is the issue of callback hell. Callback hell occurs when you have multiple nested callbacks, making the code difficult to read and maintain. This can happen when you have multiple asynchronous operations that depend on each other.
To avoid callback hell, you can use Promises or async/await, which provide a cleaner and more readable way to handle asynchronous operations.