When I first learned JavaScript's Event Loop, I kept reading explanations like:
"Asynchronous tasks are offloaded to Web APIs and their callbacks are placed into the Microtask Queue or Macrotask Queue."
I memorized the words.
But I didn't actually understand what was happening.
Then I started thinking about JavaScript like a grocery store.
JavaScript = One Cashier
Imagine a store with only one cashier.
The cashier can only serve one customer at a time.
That's JavaScript.
`console.log("A");
console.log("B");
console.log("C");`
Output:
A
B
C
Simple. One task after another.
The Problem
Now imagine a customer says:
"Wait 5 seconds before helping the next customer."
If the cashier literally waited 5 seconds, the entire store would stop moving.
That's exactly what would happen if JavaScript handled timers itself.
Browser Web APIs = Helpers
Instead, JavaScript asks a helper:
`setTimeout(() => {
console.log("Hello");
}, 5000);`
JavaScript says:
"Hey Browser, can you watch this timer for me?"
The browser helper takes the job.
JavaScript immediately continues working.
Start
End
After 5 seconds, the helper comes back and says:
"The timer finished."
Callback Queue = Waiting Line
The browser cannot interrupt JavaScript directly.
So it places the callback into a waiting line.
Browser Helper
↓
Callback Queue
The callback waits until JavaScript is free.
Event Loop = Store Manager
The Event Loop is like a manager constantly checking:
Is the cashier busy?
If yes:
Wait
If no:
Take the next customer from the waiting line
That's all the Event Loop does.
Why Does Promise Run Before setTimeout?
`console.log("Start");
setTimeout(() => {
console.log("Timeout");
}, 0);
Promise.resolve().then(() => {
console.log("Promise");
});
console.log("End");`
Output:
Start
End
Promise
Timeout
Because JavaScript has two waiting lines:
High Priority Line
- Promise.then()
- queueMicrotask()
Normal Line
- setTimeout()
- setInterval()
- DOM events
The Event Loop always clears the high-priority line first.
That's why Promise executes before setTimeout.
What About fetch()?
`fetch("/users")
.then(res => res.json())
.then(data => console.log(data));`
The browser handles the network request.
JavaScript doesn't sit around waiting for the server.
Once the response arrives, the callback gets added to the queue and runs when JavaScript is ready.
Why Do Web Workers Exist?
Imagine the cashier receives a task that takes 10 minutes.
Nobody else gets served.
The store freezes.
That's what happens when JavaScript performs heavy calculations on the main thread.
Web Workers solve this by hiring another cashier.
Heavy work runs on a separate thread while the main UI remains responsive.
Final Mental Model
Whenever you see async JavaScript:
JavaScript = Cashier
Browser Web APIs = Helpers
Callback Queue = Waiting Line
Event Loop = Manager
Web Workers = Extra Cashiers
Once this model clicks, the Event Loop becomes much easier to understand than memorizing definitions.
Top comments (1)
Did you find this post useful? Follow me for more developer-friendly content, share your thoughts in the comments, and help others discover it by sharing this post.