Hiprup

Event loop output prediction: Promise, setTimeout, and sync code

Predicting output means knowing the order: synchronous code → microtasks → macrotasks.

  1. Synchronous code runs first, top to bottom.

  2. Microtasks (Promise .then callbacks) run next, before any timer.

  3. 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.

Event loop output prediction: Promise, setTimeout, and sync code | Hiprup