Bird
Raised Fist0
Node.jsframework~10 mins

Event loop mental model in Node.js - Step-by-Step Execution

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Concept Flow - Event loop mental model
Start: Node.js program runs
Call Stack: Execute sync code
Encounter async task?
NoContinue sync
Yes
Register callback in Task Queue
Event Loop checks Call Stack empty?
No
Move callback from Task Queue to Call Stack
Execute callback
Repeat Event Loop
The event loop runs continuously, executing synchronous code first, then processing asynchronous callbacks from the task queue when the call stack is empty.
Execution Sample
Node.js
console.log('Start');
setTimeout(() => console.log('Timeout'), 0);
console.log('End');
This code logs 'Start', schedules a timeout callback, then logs 'End'. The timeout callback runs after synchronous code.
Execution Table
StepCall StackTask QueueActionOutput
1console.log('Start')[]Execute sync logStart
2setTimeout(callback, 0)[callback]Register callback in Task QueueNo output
3console.log('End')[callback]Execute sync logEnd
4empty[callback]Event Loop moves callback to Call StackNo output
5callback executionemptyExecute callback logTimeout
6emptyemptyNo more tasks, program endsNo output
💡 All synchronous code executed and task queue emptied, event loop stops.
Variable Tracker
VariableStartAfter Step 2After Step 3After Step 4After Step 5Final
Call Stackemptyemptyemptycallback executionemptyempty
Task Queueemptycallback addedcallback waitingemptyemptyempty
OutputemptyStart printedStart and End printedStart and End printedStart, End and Timeout printedAll printed
Key Moments - 2 Insights
Why does 'Timeout' print after 'End' even though setTimeout delay is 0?
Because setTimeout callback is placed in the task queue and only runs after the call stack is empty, so synchronous code like 'End' logs first (see execution_table steps 2-5).
What happens if the call stack is not empty when the event loop checks the task queue?
The event loop waits until the call stack is empty before moving callbacks from the task queue to the call stack (see concept_flow and execution_table step 4).
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what is in the call stack at Step 3?
AsetTimeout call
Bconsole.log('End')
Ccallback function
Dempty
💡 Hint
Check the 'Call Stack' column at Step 3 in the execution_table.
At which step does the event loop move the callback from the task queue to the call stack?
AStep 2
BStep 5
CStep 4
DStep 3
💡 Hint
Look for the action 'Event Loop moves callback to Call Stack' in the execution_table.
If the setTimeout delay was increased to 1000ms, how would the execution table change?
ACallback would stay longer in task queue before Step 4
BCallback would move to call stack immediately at Step 4
CCallback would execute before 'End' logs
DNo change in execution order
💡 Hint
Consider how setTimeout delay affects when callback enters the task queue and event loop timing.
Concept Snapshot
Event Loop in Node.js:
- Runs synchronous code first on Call Stack
- Async callbacks go to Task Queue
- Event Loop moves callbacks to Call Stack when empty
- setTimeout with 0 delay still runs after sync code
- Keeps Node.js non-blocking and responsive
Full Transcript
The Node.js event loop runs synchronous code first by placing it on the call stack. When it encounters asynchronous tasks like setTimeout, it registers their callbacks in the task queue. The event loop waits for the call stack to be empty before moving callbacks from the task queue to the call stack to execute them. This means even a setTimeout with zero delay runs after all synchronous code finishes. This model allows Node.js to handle many tasks efficiently without blocking the program.

Practice

(1/5)
1. Which part of the Node.js event loop runs Promise callbacks before timers?
easy
A. I/O callbacks phase
B. Timers phase
C. Microtasks queue
D. Check phase

Solution

  1. Step 1: Understand event loop phases

    The event loop has phases: timers, I/O callbacks, idle, poll, check, close callbacks, and microtasks run between phases.
  2. Step 2: Identify when Promise callbacks run

    Promise callbacks are microtasks and run immediately after the current operation, before timers and I/O callbacks.
  3. Final Answer:

    Microtasks queue -> Option C
  4. Quick Check:

    Promises run in microtasks before timers [OK]
Hint: Remember: promises run before timers in microtasks [OK]
Common Mistakes:
  • Thinking timers run before promises
  • Confusing I/O callbacks with microtasks
  • Assuming check phase runs before microtasks
2. Which of the following is the correct syntax to schedule a function to run after 0 milliseconds in Node.js?
easy
A. setTimeout(myFunc, 0);
B. setInterval(myFunc, 0);
C. process.nextTick(myFunc);
D. setImmediate(myFunc, 0);

Solution

  1. Step 1: Identify function to run after delay

    setTimeout schedules a function after a specified delay in milliseconds.
  2. Step 2: Check syntax for zero delay

    Using setTimeout(myFunc, 0) runs myFunc after the current call stack is empty, effectively scheduling it soon.
  3. Final Answer:

    setTimeout(myFunc, 0); -> Option A
  4. Quick Check:

    setTimeout with 0 delay schedules function correctly [OK]
Hint: Use setTimeout(func, 0) to schedule next tick [OK]
Common Mistakes:
  • Using setInterval for one-time delay
  • Passing extra argument to setImmediate
  • Confusing process.nextTick with setTimeout syntax
3. What will be the output order of the following code?
console.log('start');
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
console.log('end');
medium
A. promise
start
end
timeout
B. start
promise
end
timeout
C. start
end
timeout
promise
D. start
end
promise
timeout

Solution

  1. Step 1: Identify synchronous and asynchronous parts

    console.log('start') and console.log('end') run immediately (synchronously). setTimeout callback runs later. Promise callback runs as microtask after current stack.
  2. Step 2: Trace execution order

    Output order: 'start' (sync), 'end' (sync), 'promise' (microtask), 'timeout' (timer callback).
  3. Final Answer:

    start
    end
    promise
    timeout
    -> Option D
  4. Quick Check:

    Synchronous > microtasks > timers [OK]
Hint: Sync logs first, then promises, then timers [OK]
Common Mistakes:
  • Thinking promise runs after timeout
  • Mixing order of synchronous logs
  • Assuming setTimeout runs immediately
4. Consider this code snippet:
setTimeout(() => console.log('timeout'));
process.nextTick(() => console.log('nextTick'));
Promise.resolve().then(() => console.log('promise'));

Which line causes the earliest callback to run, and why might the output order be unexpected?
medium
A. process.nextTick runs earliest because it runs before microtasks
B. setTimeout runs earliest because timers run first
C. Promise.then runs earliest because promises run before nextTick
D. All callbacks run simultaneously

Solution

  1. Step 1: Understand callback priorities

    process.nextTick callbacks run immediately after the current operation, before promise microtasks and timers.
  2. Step 2: Explain output order

    Even though promises are microtasks, process.nextTick callbacks have higher priority and run first, which can surprise learners expecting promises first.
  3. Final Answer:

    process.nextTick runs earliest because it runs before microtasks -> Option A
  4. Quick Check:

    nextTick > promises > timers [OK]
Hint: nextTick runs before promises and timers [OK]
Common Mistakes:
  • Assuming timers run before nextTick
  • Confusing promise and nextTick order
  • Thinking callbacks run simultaneously
5. You want to run a CPU-heavy task without blocking the event loop in Node.js. Which approach best uses the event loop model to keep your app responsive?
hard
A. Run the task synchronously in the main thread
B. Use setTimeout to split the task into smaller chunks
C. Use process.nextTick to run the entire task immediately
D. Run the task inside a Promise without splitting

Solution

  1. Step 1: Understand event loop blocking

    Running a heavy task synchronously blocks the event loop, making the app unresponsive.
  2. Step 2: Choose non-blocking approach

    Splitting the task into smaller chunks with setTimeout allows the event loop to process other events between chunks, keeping responsiveness.
  3. Final Answer:

    Use setTimeout to split the task into smaller chunks -> Option B
  4. Quick Check:

    Split heavy tasks with timers to avoid blocking [OK]
Hint: Split heavy tasks with setTimeout to avoid blocking [OK]
Common Mistakes:
  • Running heavy tasks synchronously
  • Using nextTick for long tasks (blocks event loop)
  • Assuming promises alone prevent blocking