Quiz: Predict the Event Loop Output
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
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');
Challenge 2: Chained Microtasks
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
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
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
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.
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:
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):
console.log('script start')— script startasync1()is called:console.log('async1 start')— async1 startawait async2()→ callsasync2():console.log('async2')— async2async2returns (implicitlyPromise.resolve(undefined))
awaitsuspendsasync1. Microtask queued to resume it.
- Control returns to the script.
new Promise(executor)— executor runs synchronously:console.log('promise1')— promise1resolve()— promise fulfilled.then(() => console.log('promise2'))— microtask queued
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()suspendsasync1even thoughasync2returns immediatelyasync1 endruns beforepromise2because the microtask to resumeasync1was queued before the.thenmicrotasksetTimeoutalways 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:
- 1Synchronous code ALWAYS runs first. The entire script is one task. All sync code in that task completes before anything async.
- 2The Promise executor (function passed to new Promise()) is SYNCHRONOUS. It runs inline. Only .then/.catch/.finally callbacks are microtasks.
- 3async function code before the first await is SYNCHRONOUS. The function suspends at await and resumes via a microtask.
- 4ALL microtasks drain between tasks. Microtasks from microtasks also drain. The checkpoint runs until the queue is empty.
- 5Macrotasks (setTimeout, setInterval, I/O) run one at a time. Between each macrotask, the full microtask checkpoint runs.
- 6When in doubt, trace the algorithm: execute task → drain ALL microtasks → (maybe render) → next task. No shortcuts.