The first time I saw this little piece of code, I was confused:
console.log("Start");
setTimeout(() => console.log("Timeout"), 0);
console.log("End");
And the output was:
Start
End
Timeout
Wait… why “Timeout” after “End”? Didn’t I set the delay to 0? For me, that was the moment I realized JavaScript has its own rhythm. It doesn’t just run line by line - there’s an invisible system deciding when things happen. That system is called the event loop.
🛑 The Call Stack : Our One-Lane Road
Imagine you’re standing at a traffic signal on a narrow one-lane road. Only one car can pass at a time. Each car waits for the one in front to move before it can go. That’s exactly how JavaScript works with its call stack.
When you call a function in JavaScript, it’s like a car entering that road. The engine pushes the function onto the call stack. When the function is done, the car exits, making way for the next one.
So if I write:
function greet() {
console.log("Hello");
}
greet();
console.log("Done");
It’s just cars moving in order: greet() enters, prints “Hello”, exits, then “Done” runs. Nothing surprising here. But what if one car is a giant truck that takes forever to pass?
🚛 The Blocking Truck
Let’s say one day, instead of a small car, a giant truck pulls up to the one-lane road. It takes forever to move. Now, all the other cars behind it are stuck waiting. That’s what happens when we run heavy code in JavaScript.
function bigTask() {
for (let i = 0; i < 1e9; i++) {} // long loop
console.log("Big task done");
}
bigTask();
console.log("Next task");
Here, the loop is that giant truck. Until it finishes, JavaScript can’t do anything else - not even respond to your clicks. This is called blocking.
🚦 Helpers on the Side Road - Web APIs
Of course, if every long task blocked the road, the city (or the browser) would fall apart. That’s why the browser provides helpers, also known as Web APIs. They can take certain jobs off the main road and handle them separately.
Think of it like side roads with extra workers: one worker handles timers, another fetches data from the internet, another listens to clicks.
When you use something like setTimeout, you’re not asking JavaScript itself to wait. You’re handing that task to a helper on the side road.
console.log("Start");
setTimeout(() => console.log("Pizza ready!"), 2000);
console.log("End");
Here’s what happens:
- JavaScript logs “Start”.
- It sees setTimeout, tells a helper: “Set a 2-second timer, and when you’re done, let me know.” Then it moves on.
- It logs “End” immediately, without waiting.
- Two seconds later, the helper finishes and says, “Pizza ready!”
This way, the main road is never blocked.
🚌 The Queue at the Bus Stop
Now here’s the catch: when helpers finish their work, they don’t just cut into traffic. Imagine if someone tried to shove their car right into the one-lane road while others were driving - chaos!
Instead, completed tasks go wait at a bus stop called the task queue. The event loop is like a traffic conductor who keeps asking:
“Is the road empty yet? Okay, let the next one from the bus stop in.”
That’s why even setTimeout(fn, 0) doesn’t run instantly. It’s not that JavaScript is slow - it’s just that the callback is patiently waiting its turn at the bus stop.
🎟️ VIPs in the Fast Lane : Microtasks
But every city has VIPs, right? In JavaScript, those VIPs are promises. They don’t wait at the normal bus stop. Instead, they get their own VIP queue called the microtask queue.
The event loop always gives priority to VIPs. No matter how many regular buses (setTimeouts) are waiting, if there’s even one VIP standing by, they get in first.
console.log("Start");
setTimeout(() => console.log("Regular task"), 0);
Promise.resolve().then(() => console.log("VIP task"));
console.log("End");
The output here is:
Start
End
VIP task
Regular task
Even though the timeout had zero delay, the promise callback cut in line because microtasks always run before macrotasks.
📱 Async/Await – Ordering Food Delivery
Finally, let’s talk about async/await. Think of it like ordering food online. You place an order and then go about your life - scroll Instagram, reply to texts. When the delivery guy shows up, you pause, grab the food, and continue.
async function orderFood() {
console.log("Placing order...");
await new Promise(r => setTimeout(r, 2000));
console.log("Food delivered!");
}
orderFood();
console.log("Chatting meanwhile...");
The output is:
Placing order...
Chatting meanwhile...
Food delivered!
That’s the beauty of await: it pauses only inside that function, without freezing the entire program.
📝 Wrapping It Up
The event loop isn’t magic. It’s just a system that makes sure JavaScript doesn’t freeze when doing multiple things at once.
- The call stack is the one-lane road.
- Heavy tasks are trucks that block it.
- Web APIs are helpers with side roads.
- Finished work waits at the task queue (bus stop).
- Promises go to the microtask queue, a VIP lane.
- The event loop is the conductor making sure traffic flows smoothly.
So next time your setTimeout(fn, 0) feels “late”, remember - it’s just waiting for the road to clear.
Happy Coding :) If you liked my content - do share it with others
Top comments (0)