Callbacks in JavaScript: Simplified for Beginners

Callbacks in JavaScript: Simplified for Beginners

Let us start with understanding the powerful feature of JavaScript which is Higher-order functions.
In JavaScript, a higher-order function is a function that takes one or more functions as arguments or returns a function as its result. This means that a higher-order function either takes a function as an argument, or it returns a function, or both.

In the code below, a is a function that takes two arguments, x and fn, where fn is a function. Hence a is a higher-order function.

Now in the above code, the function which is sent as an argument to a function is called a callback function.

Synchronous and Asynchronous callbacks

Synchronous
As we all know that JavaScript is synchronous(execution happens line-by-line) and single-threaded by default.
A synchronous callback is a type of callback function that is executed immediately when it is called. In other words, the code that follows the invocation of the callback will not be executed until the callback function has completed its execution.

Let us understand this with the help of a code demo.

In the above code, as we can see from the output execution took place line-by-line. It waited for the for loop to complete its execution and then moved to the next console.log statement.

Asynchronous
An asynchronous callback is a type of callback function that is scheduled to execute at some point in the future after the calling function has completed its execution. In other words, the callback function is executed asynchronously, which means that it doesn't block the calling function from continuing its execution.

Let us understand this with the help of a code demo.

In the above code demo, Because the callback function in the setTimeout function has a delay of 3000 milliseconds, it will not be executed immediately. Instead, it will be scheduled to run after a 3-second delay.

Zero Delays

But what if the delay is 0 seconds?

Zero delay doesn't mean the call back will fire off after zero milliseconds. Calling setTimeout with a delay of 0 (zero) milliseconds doesn't execute the callback function after the given interval.

The setTimeout function is called with an anonymous function as its first argument. This anonymous function will be used as a callback function that will be executed asynchronously after the specified time interval has passed. In this case, the interval is 0 milliseconds, which means that the callback function will be executed as soon as possible, but still asynchronously.

Callback Queue and Event loop

The callback queue is a data structure that stores a list of functions that are waiting to be executed. When an asynchronous operation completes, its corresponding callback function is added to the callback queue.

The event loop runs in a loop, constantly checking the event queue for new events to process. If there are no events in the queue, the loop waits until a new event is added.

Let us take an example to understand it more clearly.

In this code, there are two callback functions created by the setTimeout function, each of which logs a message to the console. One of these callbacks has a delay of 3000 milliseconds, while the other has a delay of 0 milliseconds.

When the code is executed, the first console.log statement is executed synchronously, followed by the two setTimeout function calls. The setTimeout function schedules the execution of the callback functions at a later time and immediately returns control to the program.

The for loop executes synchronously after the second setTimeout call, which may take some time to complete. Once the for loop has finished executing, the event loop will check the Callback Queue for any pending callbacks that need to be executed.

In this case, there are two callbacks waiting in the Callback Queue: the one with a delay of 3000 milliseconds, and the one with a delay of 0 milliseconds. Since the callback with the shorter delay is ready to be executed immediately, the event loop will process it first and log "Timer done 2" to the console.

Next, the event loop will process the callback with a delay of 3000 milliseconds and log "Timer done 1" to the console.

Common Pitfalls with Callbacks

While callbacks are a powerful tool for working with asynchronous code in JavaScript, there are some common pitfalls to watch out for. Here are a few to keep in mind:

  1. Callback Hell:

Callback hell, also known as the "pyramid of doom", is a situation that arises in asynchronous programming where code becomes difficult to read and maintain due to excessive and nested callbacks.

It can be a common problem in JavaScript programming, especially when dealing with complex asynchronous code.

2. Error Handling: It's important to handle errors that might occur when calling a callback function. If you don't handle errors, your application might crash or behave unexpectedly. Always include an error handling code in your callbacks.

Conclusion

In conclusion, callbacks are an essential tool for working with asynchronous code in JavaScript. They allow us to execute code after an operation completes, handle errors, and make our code more flexible and modular.

However, when working with callbacks, it's important to watch out for common pitfalls, such as nested callbacks, callback hell, error handling, and memory leaks. By being aware of these issues, we can write better code and avoid some of the most common mistakes.

To avoid these pitfalls such as callback hell, developers can use several techniques such as promises, async/await functions, and event emitters. These techniques help to make the code cleaner, easier to read, and more maintainable, by avoiding the excessive nesting of callbacks.
We will discuss promises in the next blog.