Event Loop & Task Queues Module:

The 3 Core Behaviors You Need to Know:

1. Call Stack Rules (Synchronous World):

  • Stack executes ONE thing at a time, top to bottom
  • NOTHING else happens until stack is empty
  • All sync code (loops, functions, calculations) blocks everything

2. Queue Priority System (Asynchronous World):

  • Microtask Queue = VIP lane (Promises, queueMicrotask)
  • Callback Queue = Regular lane (setTimeout, setInterval, DOM events)
  • Animation Queue = Special lane (requestAnimationFrame - 60fps timing)

3. Event Loop’s Simple Rules:

javascript

// The Loop's Decision Making:
1. Is call stack empty? If NO → wait
2. If YES → Check microtasks first (drain completely)
3. Then check callback queue (take one)
4. Then render if needed
5. Repeat forever

The Execution Order Rules (Your Cheat Sheet):

javascript

console.log('1'); // Call stack - runs immediately
 
setTimeout(() => console.log('2'), 0); // Callback queue
 
Promise.resolve().then(() => console.log('3')); // Microtask queue
 
console.log('4'); // Call stack - runs immediately
 
// Output: 1, 4, 3, 2

Why this order?

  1. Sync code (1, 4) runs first - call stack
  2. Microtasks (3) run before callbacks - priority rule
  3. Callbacks (2) run last - lowest priority

Interview Gotchas & Nesting Rules:

Gotcha 1: Microtasks always win

javascript

setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => {
    console.log('promise');
    setTimeout(() => console.log('nested timeout'), 0);
});
// Output: promise, timeout, nested timeout

Gotcha 2: Nested microtasks create “starvation”

javascript

function recursiveMicrotask() {
    Promise.resolve().then(() => {
        console.log('microtask');
        recursiveMicrotask(); // Creates infinite microtasks
    });
}
setTimeout(() => console.log('I will never run'), 0);
recursiveMicrotask(); // Blocks everything!

Gotcha 3: Complex nesting - just follow the rules

javascript

setTimeout(() => {
    console.log('outer timeout');
    Promise.resolve().then(() => {
        console.log('inner promise');
        setTimeout(() => console.log('deepest timeout'), 0);
    });
}, 0);
 
Promise.resolve().then(() => {
    console.log('outer promise');
    setTimeout(() => console.log('inner timeout'), 0);
});
 
// Output: outer promise, outer timeout, inner promise, inner timeout, deepest timeout

Your Mental Framework for Any Complexity:

Step 1: Identify all sync code → runs immediately in order Step 2: Find all async code → sort by queue type Step 3: Apply priority: Microtasks drain completely, then one callback, then repeat

For nested scenarios: When something executes, it can ADD new tasks to queues. Just re-apply the rules from that point.

Bottom Line:

Event loop is like a traffic controller with strict rules: Sync code goes first, microtasks are VIP, callbacks wait in line. No matter how deep the nesting, just follow the queue priority and you’ll never get tricked. The key is understanding that each execution can add NEW tasks to queues - just re-apply the same rules from that moment.


The Box Method (Your Interview Superpower):

original question :

setTimeout(() => {
    console.log('outer timeout');
    Promise.resolve().then(() => {
        console.log('inner promise');
        setTimeout(() => console.log('deepest timeout'), 0);
    });
}, 0);

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

// Output: outer promise, outer timeout, inner promise, inner timeout, deepest timeout

Draw 3 boxes on paper/whiteboard:

[Call Stack]    [Microtask Queue]    [Callback Queue]
    |               |                    |
    |               |                    |

Step-by-Step Tracking for Your Example:

Initial State - JS reads the code:

setTimeout(() => { /* outer timeout code */ }, 0);  // Found first!
Promise.resolve().then(() => { /* outer promise code */ });  // Found second!

Box State After Reading:

[Call Stack]    [Microtask Queue]         [Callback Queue]
   empty         outer promise            outer timeout

Event Loop Starts - Rule: Microtask ALWAYS first

Step 1: Execute outer promise

[Call Stack]           [Microtask Queue]    [Callback Queue]
outer promise code      empty                outer timeout

Inside outer promise: console.log('outer promise') + new setTimeout

  • Log prints: “outer promise”
  • New setTimeout goes to callback queue

Box State After Step 1:

[Call Stack]    [Microtask Queue]    [Callback Queue]
   empty           empty              1. outer timeout
                                     2. inner timeout

Step 2: Execute outer timeout (first in callback queue)

[Call Stack]           [Microtask Queue]    [Callback Queue]
outer timeout code        empty              inner timeout

Inside outer timeout: console.log('outer timeout') + new Promise

  • Log prints: “outer timeout”
  • New Promise goes to microtask queue

Box State After Step 2:

[Call Stack]    [Microtask Queue]    [Callback Queue]
   empty         inner promise        1. inner timeout
                                     2. deepest timeout

Step 3: Execute inner promise (microtask has priority!)

  • Log prints: “inner promise”
  • New setTimeout goes to callback queue

Final executions:

  • “inner timeout” (callback queue position 1)
  • “deepest timeout” (callback queue position 2)

Your Simple Rules to Never Get Confused:

Rule 1: Read & Sort First

  • Don’t execute anything mentally
  • Just put code in the right boxes

Rule 2: Execute One Box at a Time

  • Microtask box → drain completely
  • Callback box → take ONE item
  • Repeat

Rule 3: When Code Executes, It Can Add New Items

  • Just update your boxes
  • Apply rules again

Quick Practice Method:

For any complex code:

  1. Scan - put everything in boxes first
  2. Execute - follow box priority
  3. Update - when code runs, it might add new items to boxes
  4. Repeat - keep following the rules

Mental Shortcut for Interviews:

Instead of thinking “what executes when”: Think “what’s in each box right now”

The brain trick:

  • Don’t try to predict the future
  • Just manage the current state of your 3 boxes
  • Let the event loop rules handle the rest

Bottom Line: Use the box method on paper during interviews. It transforms chaos into a simple sorting + priority system. Your brain won’t get overwhelmed because you’re just moving items between boxes, not trying to simulate a computer!