Skip to content

Quiz: Predict the Event Loop Output

intermediate12 min read

Test Your Event Loop Mental Model

Alright, time to put your knowledge to the test. These challenges are ordered by difficulty, and each one tests a different aspect of the event loop. For each, predict the exact console output before looking at the answer. Writing down your answer before checking builds stronger recall (testing effect -- Roediger & Karpicke 2006).

One rule: trace through the event loop algorithm step by step. Don't guess. Don't rely on memorized patterns. Walk through the machine.


Challenge 1: The Basics

Quiz
What is the output?
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');

Challenge 2: Chained Microtasks

Quiz
What is the output?
console.log('1');
setTimeout(() => console.log('3'), 0);
Promise.resolve().then(() => console.log('2'));
queueMicrotask(() => console.log('4'));
console.log('5');

Challenge 3: Microtasks Spawning Microtasks

Quiz
What is the output?
console.log('A');
setTimeout(() => console.log('C'), 0);
Promise.resolve().then(() => {
  console.log('B');
  queueMicrotask(() => console.log('E'));
});
console.log('D');

Challenge 4: async/await Timing

Quiz
What is the output?
async function foo() {
  console.log('2');
  await Promise.resolve();
  console.log('3');
  await Promise.resolve();
  console.log('4');
}

console.log('1');
foo();
console.log('5');
Promise.resolve().then(() => console.log('6'));

Challenge 5: Promise Executor Is Synchronous

Quiz
What is the output?
console.log('start');

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

console.log('end');

Challenge 6: Nested Timeouts and Promises

Now we're getting spicy. This is where the event loop model really matters. Trace it carefully.

Quiz
What is the output?
console.log('1');

setTimeout(() => {
  console.log('2');
  Promise.resolve().then(() => console.log('3'));
}, 0);

Promise.resolve().then(() => {
  console.log('4');
  setTimeout(() => console.log('5'), 0);
});

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

console.log('7');
queueMicrotask(() => console.log('8'));

Let's trace this one step by step:

Execution Trace
Sync
console.log('1')
Output: 1
Register
setTimeout A (logs 2, schedules promise for 3) → macrotask queue
Macrotask queue: [A]
Register
Promise.then (logs 4, schedules setTimeout for 5) → microtask queue
Microtask queue: [P1]
Register
setTimeout B (logs 6) → macrotask queue
Macrotask queue: [A, B]
Sync
console.log('7')
Output: 1, 7
Register
queueMicrotask (logs 8) → microtask queue
Microtask queue: [P1, M1]
Drain
Script ends. Microtask checkpoint begins.
Processing microtask queue...
Micro 1
P1 runs: logs '4', registers setTimeout C (logs 5)
Output: 1, 7, 4 | Macrotask queue: [A, B, C]
Micro 2
M1 runs: logs '8'
Output: 1, 7, 4, 8
Task A
Macrotask A: logs '2', registers Promise.then (logs 3)
Output: 1, 7, 4, 8, 2
Drain
Microtask checkpoint: logs '3'
Output: 1, 7, 4, 8, 2, 3
Task B
Macrotask B: logs '6'
Output: 1, 7, 4, 8, 2, 3, 6
Task C
Macrotask C: logs '5'
Output: 1, 7, 4, 8, 2, 3, 6, 5

The complete output is: 1, 7, 4, 8, 2, 3, 6, 5

Notice how the quiz options above are intentionally tricky. The execution trace reveals the actual answer when you follow the algorithm mechanically. This is why tracing beats guessing -- every time.


Challenge 7: The Ultimate Event Loop Test

This is the final boss. It combines every concept: sync code, promises, async/await, setTimeout, queueMicrotask, and Promise executor semantics.

Challenge: Predict the Complete Output

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');
Show Answer

Output:

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

Detailed trace:

Synchronous execution (the script task):

  1. console.log('script start')script start
  2. async1() is called:
    • console.log('async1 start')async1 start
    • await async2() → calls async2():
      • console.log('async2')async2
      • async2 returns (implicitly Promise.resolve(undefined))
    • await suspends async1. Microtask queued to resume it.
  3. Control returns to the script.
  4. new Promise(executor) — executor runs synchronously:
    • console.log('promise1')promise1
    • resolve() — promise fulfilled
    • .then(() => console.log('promise2')) — microtask queued
  5. console.log('script end')script end

Microtask checkpoint: 6. Resume async1 after await: console.log('async1 end')async1 end 7. Promise .then: console.log('promise2')promise2

Macrotask: 8. setTimeout: console.log('setTimeout')setTimeout

Key insights:

  • Everything in the Promise executor is synchronous
  • await async2() suspends async1 even though async2 returns immediately
  • async1 end runs before promise2 because the microtask to resume async1 was queued before the .then microtask
  • setTimeout always runs last because it's a macrotask

Bonus: The Rule Sheet

If you got all 7 correct on first attempt, you have a seriously solid event loop mental model. If not, no stress -- here are the rules to internalize:

Key Rules
  1. 1Synchronous code ALWAYS runs first. The entire script is one task. All sync code in that task completes before anything async.
  2. 2The Promise executor (function passed to new Promise()) is SYNCHRONOUS. It runs inline. Only .then/.catch/.finally callbacks are microtasks.
  3. 3async function code before the first await is SYNCHRONOUS. The function suspends at await and resumes via a microtask.
  4. 4ALL microtasks drain between tasks. Microtasks from microtasks also drain. The checkpoint runs until the queue is empty.
  5. 5Macrotasks (setTimeout, setInterval, I/O) run one at a time. Between each macrotask, the full microtask checkpoint runs.
  6. 6When in doubt, trace the algorithm: execute task → drain ALL microtasks → (maybe render) → next task. No shortcuts.