ReviseAlgo Logo

JavaScript Interview Prep

Event Loop Execution Order Questions

Master event loop execution order interview questions. Learn to trace microtask vs macrotask execution timelines step-by-step.

Last Updated: July 29, 2026 12 min read

1. Introduction

Event Loop execution order questions test your ability to trace asynchronous code line-by-line. To solve these puzzles, you must track synchronous code execution on the Call Stack, followed by Microtasks (Promises, queueMicrotask), and finally Macrotasks (setTimeout, setInterval).

2. Golden Rules of Execution Order

  • 1. Synchronous Code First: Code inside main scripts or inside new Promise((resolve) => { ... }) executors runs synchronously on the Call Stack immediately.
  • 2. Microtask Queue Drain: When the Call Stack empties, the Event Loop drains the ENTIRE Microtask Queue (Promise .then/catch/finally callbacks, queueMicrotask).
  • 3. Macrotask Queue (One per tick): After all microtasks finish, the Event Loop takes the oldest single Macrotask (setTimeout, setInterval).
  • 4. Repeat: After processing the macrotask, the Event Loop checks the Microtask Queue again before taking the next macrotask.

3. Essential Event Loop Puzzles

Puzzle 1: Basic Promise Executor vs SetTimeout

console.log('1');

setTimeout(() => { console.log('2'); }, 0);

new Promise((resolve) => { console.log('3'); resolve(); }).then(() => { console.log('4'); });

console.log('5');

View Step-by-Step Execution

Output: "1", "3", "5", "4", "2"

Step-by-Step Breakdown:
1. Synchronous: console.log('1') -> logs 1.
2. setTimeout callback is pushed to Macrotask Queue.
3. new Promise executor runs synchronously: console.log('3') -> logs 3. resolve() queues .then() callback to Microtask Queue.
4. Synchronous: console.log('5') -> logs 5. Call Stack is now empty!
5. Drain Microtasks: .then() callback runs -> logs 4.
6. Process Macrotask: setTimeout callback runs -> logs 2.

Puzzle 2: Async / Await Unwrapping

async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}

async function async2() { console.log('async2'); }

console.log('script start');

setTimeout(() => { console.log('setTimeout'); }, 0);

async1();

new Promise((resolve) => { console.log('promise1'); resolve(); }).then(() => { console.log('promise2'); });

console.log('script end');

View Step-by-Step Execution

Output:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

Key Insight: await async2() runs async2() synchronously, then queues code following await into the Microtask Queue!

4. Quick Quiz

Q1: Code inside a new Promise constructor callback executes in which phase?

A) Microtask Queue

B) Synchronously on the Call Stack

Answer: B — The executor function passed to new Promise((resolve, reject) => { ... }) executes synchronously immediately.

5. Production Considerations

  • Avoid Starving the Macrotask Queue: Recursively queuing microtasks (e.g. infinite queueMicrotask loops) blocks the Event Loop from processing macrotasks or browser rendering, causing UI freezes.