Output Quiz #10 — setTimeout + Promises: Real-World Interleaving

10 output questions tracing exact order when setTimeout callbacks, Promise chains, and async functions mix — the patterns that appear in every senior JS interview.

10 min read
JavaScript
Interview
Output

TABLE OF CONTENTS
Output Quiz #10 — setTimeout + Promises: Real-World Interleaving

The hardest interview questions combine macrotasks and microtasks in a single snippet — setTimeout inside .then(), Promise.resolve() inside setTimeout, async functions called from timer callbacks. Ten questions building from two-queue basics to full multi-layer traces.


Q1 — setTimeout inside a .then() handler

Loading editor...

Show answer

Output:

1
3
4
2

Why: The microtask .then() runs first: 1, schedules setTimeout(2), 3. Then the macrotask queue is drained. Both setTimeout(4) and setTimeout(2) are in the queue. setTimeout(4) was scheduled first (during sync), so it runs first: 4. Then setTimeout(2) runs: 2. Macrotasks are FIFO.


Q2 — Promise.resolve() inside a setTimeout callback

Loading editor...

Show answer

Output:

macro
micro inside macro
next macro

Why: First macrotask runs: logs "macro", then schedules a new microtask. After the macrotask completes, the engine drains the microtask queue before starting the next macrotask. So "micro inside macro" runs, then "next macro". This is the golden rule: after every macrotask, all microtasks are drained before the next macrotask.


Q3 — Two setTimeouts + one Promise

Loading editor...

Show answer

Output:

sync
promise
timeout1
timeout2

Why: Sync code runs first ("sync"). Both setTimeouts queue macrotasks (in order). The .then() queues a microtask. After sync: microtask queue is drained ("promise"). Then macrotask queue is drained in FIFO order: "timeout1", "timeout2".


Q4 — Promise chain → setTimeout → Promise chain

Loading editor...

Show answer

Output:

B
E
A
C
D

Why: Sync queues: macrotasks [A, C], microtasks [B, E]. Microtasks drain: B, E. First macrotask: A. Second macrotask: C, then queues microtask D. Microtask drains before next macrotask: D. The microtask from inside a macrotask always runs before the next macrotask in line.


Q5 — setTimeout with delay vs resolving Promise after shorter duration

Loading editor...

Show answer

Output:

promise
timeout

Why: At ~50ms, the inner setTimeout resolves the Promise, and its .then() callback is scheduled as a microtask — which runs immediately after the current task (the 50ms timer) completes. At ~100ms, the outer setTimeout fires. Even though the outer setTimeout was registered first, the Promise's .then() fires as a microtask at 50ms — long before the 100ms macrotask. Macrotask ordering is by due time, not registration order.


Q6 — Three setTimeouts with same delay + Promise chain

Loading editor...

Show answer

Output:

micro
one
two
three

Why: All three setTimeouts are queued as macrotasks in registration order (one, two, three). The .then() is queued as a microtask. Sync ends → microtask drains ("micro") → macrotasks drain in FIFO order ("one", "two", "three"). The delay being 0 for all doesn't change ordering — macrotasks are dequeued in insertion order.


Q7 — Async function called from a setTimeout callback

Loading editor...

Show answer

Output:

micro
timeout start
fetch start
timeout end
fetch end

Why: Microtask "micro" runs first. Then the macrotask fires: "timeout start" logs. fetchData() is called — its synchronous part ("fetch start") runs immediately. Then await Promise.resolve() suspends it. "timeout end" logs (still part of the macrotask). After this macrotask, the microtask queue is drained: "fetch end" runs. The resumed async function continuation is a microtask from the macrotask that started it.


Q8 — Promise.all inside a setTimeout

Loading editor...

Show answer

Output:

D
A
B C

Why: Micromask D runs first. Then the macrotask fires: "A" logs. await Promise.all(...) suspends the async function — the rest becomes a microtask. The macrotask ends. Engine drains microtask queue: the continuation runs, logs "B C". Note: Promise.all with already-resolved Promises resolves synchronously within the microtask scheduling layer, so the await resumes in the same microtask drain cycle.


Q9 — Nested setTimeout + Promise.resolve()

Loading editor...

Show answer

Output:

T1
P1
T2
P2

Why: First macrotask: "T1" logs. It queues a new macrotask T2 and a microtask P1. After the macrotask exits, microtasks drain: "P1". Then the second macrotask: "T2", which queues microtask "P2". After that macrotask exits, microtasks drain: "P2". The outer pattern repeats — microtask always drains after each macrotask.


Q10 — Full multi-layer trace

Loading editor...

Show answer

Output:

sync-1
sync-2
p1
qm
t1
p-in-t1
t3
t2
t-in-p-in-t1

Why: Step-by-step trace:

  1. Sync: "sync-1", "sync-2". Macrotask queue: [t1, t3]. Microtask queue: [p1].
  2. Drain microtasks: "p1" runs, queues qm. Then "qm" runs.
  3. First macrotask t1: logs "t1", queues microtask p-in-t1, queues macrotask t2. Macrotask queue now: [t3, t2].
  4. Drain microtasks: "p-in-t1" runs, queues macrotask t-in-p-in-t1. Queue: [t3, t2, t-in-p-in-t1].
  5. Next macrotask t3: "t3".
  6. Next macrotask t2: "t2".
  7. Next macrotask t-in-p-in-t1: "t-in-p-in-t1".

Key Rules

ScenarioOrder
setTimeout inside .then()Macrotask goes to back of task queue — runs after previously queued macrotasks
Promise.resolve() inside setTimeoutMicrotask runs before the next macrotask
Sync + N setTimeout(0) + PromiseSync → all microtasks → macrotasks in FIFO order
setTimeout(100) vs Promise resolving at 50msPromise .then() (micro at 50ms) fires before setTimeout (macro at 100ms)
async fn() from setTimeoutPre-await code is part of the macrotask; post-await is a microtask
Promise.all inside setTimeoutResolved values are microtasks; all run before next macrotask

Golden rule: After every macrotask, the microtask queue is completely drained before the next macrotask runs.


Go Deeper


Let's Connect

© 2026 Naveen Karthik // Built with React & MUI