Event loop output prediction: Promise, setTimeout, and sync code
Predicting output means knowing the order: synchronous code → microtasks → macrotasks.
Synchronous code runs first, top to bottom.
Microtasks (Promise .then callbacks) run next, before any timer.
Macrotasks (setTimeout) run last, one per loop.
So a setTimeout(…, 0) always logs after a Promise callback, even though both look "immediate".
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);
});
Promise.resolve().then(() => console.log('6'));
console.log('7');
// Output: 1, 7, 4, 6, 2, 3, 5
// Explanation:
// Sync: 1, 7 (call stack)
// Microtask: 4, 6 (Promise.then — ALL drain before macrotask)
// Macrotask: 2 (first setTimeout)
// Microtask: 3 (Promise inside setTimeout — drains before next macro)
// Macrotask: 5 (setTimeout scheduled inside Promise)1 and 7 run synchronously (call stack). Then ALL microtasks drain: 4 (first Promise.then), 6 (second Promise.then). Then one macrotask: 2 (setTimeout).
Then its microtask: 3 (Promise inside setTimeout). Then the next macrotask: 5 (setTimeout scheduled from inside the first Promise). Key rule: ALL microtasks drain before the next macrotask.
Trace through step by step: sync first, then ALL microtasks, then ONE macrotask, then ALL microtasks, then next macrotask. Practice with multiple variations.
This question appears in nearly every JS interview. The microtask inside a macrotask (3 after 2) is the detail most get wrong.