Understanding the Event Loop, Call Stack, and Callback Queue in JavaScript

May 7, 2024

JavaScript is a single-threaded programming language, which means it can execute one piece of code at a time. However, it handles asynchronous operations efficiently, making it appear concurrent. To understand this, we need to dive into the concepts of the event loop, call stack, and callback queue.

The Call Stack

The call stack is a data structure that keeps track of function calls. When a function is called, it is added to the top of the call stack. When the function completes, it is removed from the stack.

function first() {
    console.log('First');
}

function second() {
    first();
    console.log('Second');
}

second();

This process continues for each function call, ensuring that functions are executed in order.

The Event Loop

The event loop is the mechanism that handles asynchronous operations in JavaScript. It continuously checks the call stack and the callback queue, ensuring that the call stack is empty before pushing any callback functions from the callback queue to the call stack for execution.

The Callback Queue

The callback queue is where all the asynchronous operations (like setTimeout, Promises, or event handlers) wait to be executed. When an asynchronous operation completes, its callback function is added to the callback queue.

console.log('Start');

setTimeout(() => {
    console.log('Callback');
}, 2000);

console.log('End');

Bringing It All Together

Let's see a more complex example to understand how the event loop, call stack, and callback queue work together.

Example:

console.log('Start');

setTimeout(() => {
    console.log('Timeout 1');
}, 0);

Promise.resolve().then(() => {
    console.log('Promise 1');
}).then(() => {
    console.log('Promise 2');
});

setTimeout(() => {
    console.log('Timeout 2');
}, 0);

console.log('End');

Execution Order:

Conclusion

Understanding these concepts helps in writing efficient asynchronous JavaScript and debugging complex code. This is how JavaScript maintains a non-blocking, asynchronous environment despite being single-threaded.