Ever wondered why your code sometimes runs in a different order than you expected?
Like:
“Why did this run before that?!” 😵💫
Congratulations — you just met one of the most important parts of JavaScript:
✨ The Event Loop
Let’s break it down super simply. No computer science degree required. 🧠🍬
🚦 JavaScript Is a One-Lane Road
JavaScript runs on one main thread — imagine a tiny road with one single lane.
Only one car (piece of code) can drive at a time. No overtaking. No shortcuts.
console.log("🚗 Car 1");
console.log("🚗 Car 2");
console.log("🚗 Car 3");
Output:
🚗 Car 1
🚗 Car 2
🚗 Car 3
Easy. One after another.
😬 But What If One Car Stops?
What if a car pulls over to refuel ⛽️?
If it just stopped on the road, everything behind it would be stuck.
JavaScript says:
“Go refuel somewhere else — I’ll keep the road free.” ✅
That’s asynchronous (async) code.
console.log("⛽️ Refueling...");
setTimeout(() => {
console.log("✅ Refuel done!");
}, 2000);
console.log("🚗 Another car passing by");
Output:
⛽️ Refueling...
🚗 Another car passing by
✅ Refuel done!
Even though setTimeout appears first, it finishes later — because JavaScript moves it off the main road, so the rest of the code can keep running.
🔁 So… What Is the Event Loop?
Here’s what happens behind the scenes:
| Thing | What it does | Emoji |
|---|---|---|
| Call Stack | The main road where JS runs code line by line | 🛣️ |
| Web APIs (browser) / libuv (Node.js) | Parking lot for async work | 🅿️ |
| Callback Queue | Regular waiting line for async callbacks | 📬 |
| Microtask Queue | VIP fast-lane (for Promises, mutation observers, etc.) | 🚓 |
| Event Loop | The traffic officer that decides who enters the road next | 👮 |
🧪 Try This in the Console
console.log("1️⃣ Start");
setTimeout(() => console.log("4️⃣ Timeout done"), 0);
Promise.resolve().then(() => console.log("3️⃣ Promise done"));
console.log("2️⃣ End");
Output:
1️⃣ Start
2️⃣ End
3️⃣ Promise done
4️⃣ Timeout done
Why? 🤔
-
Promise.then()goes to the microtask queue (high priority ✅) -
setTimeout()goes to the callback queue (lower priority ⏳) - The Event Loop always empties the microtask queue first
Even if setTimeout(..., 0) has 0 ms delay, it still waits behind microtasks.
📌 Visual Summary
Your Code → Call Stack
setTimeout → Web API / libuv
↓ (after delay)
Callback Queue
Promise.then → Microtask Queue ← VIP priority
Event Loop → Moves tasks to Call Stack when it's empty
✅ Microtasks always run before callbacks
❗ setTimeout(..., 0) never means “run instantly”
💡 Bonus: What About Web Workers?
So far, we said JavaScript has only one lane (single thread).
But there is a way to get more lanes:
👷 Web Workers
- Run JavaScript in separate threads ✅
- Don’t block the main thread ✅
- Cannot access the DOM directly ❌
- Communicate with the main thread using
postMessage📩 (no shared memory)
Perfect for:
- Heavy calculations 🧮
- Image processing 🖼️
- Long loops 🔁
They don’t change the Event Loop — they’re just extra helpers doing work in parallel.
🧠 TL;DR (Like You're 5)
| Concept | Meaning | Emoji |
|---|---|---|
| Call Stack | Main road | 🛣️ |
| Web APIs / libuv | Parking lot for async | 🅿️ |
| Callback Queue | Cars waiting in line | 🚗📬 |
| Microtask Queue | VIP instant lane (Promises) | 🚓 |
| Event Loop | Traffic cop | 👮 |
| Web Worker | Extra road (no DOM access) | 🛣️🛠️ |
✨ Final Thought
JavaScript isn’t slow — it’s organized.
It never blocks the road if it doesn’t have to.
The Event Loop makes async feel like magic… until you understand it.
Then it stops being magic and becomes predictable. 🔁💙
👋 Hi, I'm Sylwia
I break code so you don't have to. 🛠️💥
If you enjoy laughing at past bugs and learning from them, follow me here or on my Substack:
(Yes, I write about real bugs. Yes, most of them were my fault. You're welcome. 😅)
Top comments (0)