Part 5: The JavaScript Event Loop Trap That Broke My Mental Model

If you think you understand async… this one will drive you crazy.

Part 5: The JavaScript Event Loop Trap That Broke My Mental Model

The Setup

You’re asked to predict the output of this simple-looking code:

console.log('start');
setTimeout(() => {
console.log('timeout');
}, 0);
Promise.resolve().then(() => {
console.log('promise');
});
console.log('end');

Easy?

Wait until you get asked why.

What’s the Output?

Before we reveal it, pause and guess.

You’d be surprised how many devs (including seniors!) get this wrong in interviews.

Have you figured it out? Then comment your answer without reading further.

🔍 Here’s the Actual Output:

start 
end
promise
timeout

Why?

Confusing GIF

Because this isn’t about setTimeout vs Promise.

It’s about macrotasks vs microtasks.

Let’s break it:

  • console.log('start') → Runs immediately (synchronous)
  • setTimeout(..., 0) → Scheduled as a macrotask
  • Promise.then(...) → Scheduled as a microtask
  • console.log('end') → Runs immediately
  • Microtasks get executed before the next macrotask → So 'promise' prints before 'timeout'

Microtasks > Macrotasks

Think of it like this:

Call Stack → Microtask Queue → Macrotask Queue

So even if setTimeout is 0ms, microtasks like Promise.then() still run first.

This breaks a lot of mental models because we assume “timeout 0 = instant.”

But it’s not.

Real-World Bug I Faced

In a production dashboard, I had a loading spinner that would toggle off using a setTimeout.

Meanwhile, some microtask-driven rendering logic (via Promise.then) was updating UI state.

The result?

The spinner disappeared before the DOM was ready. Caused a flicker bug. Took hours to trace.

Real Bug I faced

Fix? Understand the Queue

If you want predictable async behavior:

  • Use await or Promise chains
  • Be cautious with setTimeout if microtasks are also at play
  • Know that requestAnimationFrame, setTimeout, and I/O callbacks go in the macrotask queue
  • Promise.then, MutationObserver, and queueMicrotask go in the microtask queue

Interview Tip

The next time you’re given a similar event loop snippet:

  1. Read it line-by-line.
  2. Note which ones are sync, microtask, or macrotask.
  3. Predict based on the queue order — not gut feel.

Your Challenge

What does this log?

console.log('1');
queueMicrotask(() => console.log('2'));
Promise.resolve().then(() => console.log('3'));
setTimeout(() => console.log('4'), 0);
console.log('5');

(No cheating — comment your guess 👇)

Over to You

Event loop tricks like these are what turn decent devs into top-tier engineers. Have you ever faced a weird async bug? Share it.

At Dev Simplified, We Value Your Feedback 📊

👉 Follow us to not miss any updates.

👉 Have any suggestions? Let us know in the comments!

👉 Subscribe for free and join our growing community!