MediumQuince

What gets logged?

Prompt

What is the order of the console logs when the following code runs?

setTimeout(() => console.log(1));

Promise.resolve().then(() => console.log(2));

Promise.resolve().then(() =>
setTimeout(() => console.log(3))
);

new Promise(() => console.log(4));

setTimeout(() => console.log(5));
Hint 1

JavaScript has two types of async queues: the microtask queue (for Promise callbacks like .then()) and the macrotask queue (for setTimeout callbacks). The event loop always drains all microtasks before processing the next macrotask.

Hint 2

The function you pass to new Promise(fn) is called the executor. It runs synchronously, immediately when the Promise is created. It's not queued anywhere. So new Promise(() => console.log(4)) logs 4 during the initial synchronous pass, not later.

Solution

The output order is: 4, 2, 1, 5, 3.

Explanation

This is the kind of question that separates candidates who have memorized "setTimeout is async" from those who actually understand how the event loop prioritizes work. The answer feels counterintuitive until you see the full picture.

Before we trace through the code, here's the one mental model you need. JavaScript has two waiting lines for async callbacks. Microtasks (Promise .then() callbacks) go into a high-priority queue. Macrotasks (setTimeout callbacks) go into a lower-priority queue. After all synchronous code finishes, the event loop drains the entire microtask queue first, and only then picks up the next macrotask. Think of microtasks as the VIP line at a club.

Synchronous pass (top to bottom)

JavaScript runs all synchronous code first, from top to bottom. Nothing async fires yet.

Line 1: setTimeout(() => console.log(1)) schedules a callback into the macrotask queue. Nothing logged.

Line 2: Promise.resolve().then(() => console.log(2)) — the Promise is already resolved, so the .then() callback is queued into the microtask queue. Nothing logged yet.

Line 3: Promise.resolve().then(() => setTimeout(() => console.log(3))) — same deal, the .then() callback goes to the microtask queue. But notice: the setTimeout inside it hasn't run yet. That only happens when this microtask executes later.

Line 4: new Promise(() => console.log(4)) — here's where people get tripped up. The function you pass into new Promise() (the executor) runs synchronously, right then and there. It's not queued, it's not deferred. It just runs. Logs 4.

Line 5: setTimeout(() => console.log(5)) schedules another macrotask. Nothing logged.

After the synchronous pass: 4

Microtask queue

The synchronous code is done. Before touching any setTimeout callbacks, the event loop processes every microtask:

  1. () => console.log(2)Logs 2.
  2. () => setTimeout(() => console.log(3)) — this runs the setTimeout, which schedules console.log(3) as a new macrotask. Nothing logged from this one, but it adds 3 to the end of the macrotask line.

After microtasks: 4, 2

Macrotask queue

Now the event loop works through macrotasks one at a time:

  1. () => console.log(1)Logs 1. (scheduled on line 1)
  2. () => console.log(5)Logs 5. (scheduled on line 5)
  3. () => console.log(3)Logs 3. (scheduled during microtask processing, so it's last in line)

Final output: 4, 2, 1, 5, 3

Where candidates get tripped up

The most common mistake is thinking new Promise(() => console.log(4)) is async. It's not. The executor runs synchronously. Only .then() callbacks are queued.

The second mistake is getting the order of 1, 5, 3 wrong. People assume 3 should come before 5 because line 3 appears before line 5 in the code. But console.log(3) wasn't scheduled during the synchronous pass. It was scheduled during microtask processing, which happens after all synchronous code (including line 5's setTimeout) has already run. So 3 enters the macrotask queue last.

Understanding this priority system (synchronous > microtasks > macrotasks) is genuinely useful beyond interviews. It explains why Promise.then callbacks fire before setTimeout(..., 0), why MutationObserver callbacks (also microtasks) can cause unexpected ordering, and why queueMicrotask() exists as an explicit API for this priority level.

Playground