Event Loop
How the JavaScript Event Loop Works
When we start learning JavaScript, everything seems straightforward—you call a function, it executes, then the next one runs. But then comes a moment when you write `setTimeout`, `Promise`, or `fetch`, and you feel something isn’t right—why is the order of the code breaking? This is exactly where the Event Loop comes into play.
In this article, I’ll explain what the Event Loop is, why it’s needed, and how it actually works behind the scenes. Everything with simple examples.
Imagine JavaScript working in a queue—executing one operation at a time. This is called the call stack. It’s a place where all the instructions to be executed are placed.
function greet() {
console.log("Hello world");
}
greet(); // Will execute the greet function
First, `greet()` is placed in the stack. JavaScript enters the function, executes `console.log`, and when it finishes, it removes the function from the stack. Everything happens in order.
Up to this point, everything is clear. But when you perform an asynchronous operation, like `setTimeout`, things work a little differently.
console.log("Start");
setTimeout(() => {
console.log("After Timeout");
}, 1000);
console.log("End");
You might think, “Okay, this code will run in order: Start, then After Timeout, then End.” But no, it will print:
Start End After Timeout
Why? Because `setTimeout` is sent to a temporary "environment" that we don’t see. This is called Web APIs (or its equivalent in Node.js). When the 1 second passes, it says, “I’m ready,” and the callback is placed in the task queue, waiting for its turn.
JavaScript continues working only when the call stack is empty. And when the time comes, the Event Loop takes the callback from the queue and puts it back into the call stack to execute.
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
console.log("C");
What will happen? The result will be:
A C B
Even though the timeout is 0, it doesn’t mean it will execute immediately. It waits for the call stack to clear. This means JavaScript never interrupts current execution, even if the timeout is "0".
Promises and async/await belong to a world called microtasks. When you use a Promise in your code, its callback isn’t placed in the same queue as `setTimeout`.
console.log("Start");
Promise.resolve().then(() => {
console.log("After Promise");
});
setTimeout(() => {
console.log("After Timeout");
}, 0);
console.log("End");
The result will be:
Start End After Promise After Timeout
How this works: JavaScript finishes the synchronous code (`Start` and `End`), then checks if there are any tasks in the microtask queue. If yes, it executes them (the Promise), and only then moves to the timeout.
The Event Loop is something that constantly loops in the following way:
This way, JavaScript can wait for operations (e.g., a server response) without freezing the entire program.
The JavaScript Event Loop is a hidden mechanism that allows us to write asynchronous code without multithreading. By understanding the call stack, microtasks, and task queue, you’ll be able to predict your code’s execution and write clearer, more stable async programs.
If you remember these three rules, you’ll always understand what’s happening: