The Event Loop, Visualized

Promise vs setTimeout — interactive event loop example

1console.log('start');
2
3setTimeout(() => {
4 console.log('timeout');
5}, 0);
6
7Promise.resolve().then(() => {
8 console.log('promise');
9});
10
11console.log('end');

The script starts executing top to bottom on the call stack. console.log("start") runs synchronously and prints right away.

1 / 9

Call stack

script

Web APIs — browser land

The event loop — is the call stack empty? → drain the microtask queue first → then take one callback.

Microtask queue — priority

Callback queue

Console

01start

Fig. — Promise vs setTimeout · step 1 of 9

What is going on here

This is the interview question, and the answer is start, end, promise, timeout. Both callbacks are scheduled at essentially the same moment, and the timer even becomes ready first — yet the promise callback still wins.

The reason is that they are not in the same queue. setTimeout produces a task; a resolved promise produces a microtask. When the script finishes and the stack empties, the loop performs a microtask checkpoint before it will select any task, and that checkpoint drains the microtask queue completely. Only then does one task get to run.

Two details make this more than trivia. First, "drains completely" includes microtasks queued by other microtasks, which is why a chain of .then callbacks all resolves before a single waiting timer. Second, the checkpoint happens after every task rather than once per script, so this ordering repeats on every turn of the loop, not just at startup.

Common questions

Why does a promise callback run before setTimeout?

Promise callbacks go on the microtask queue and setTimeout callbacks go on the task queue. The event loop drains the entire microtask queue before it selects a task, so the promise wins even when the timer became ready earlier.

What is the difference between a task and a microtask?

A task is one unit of work the loop selects per iteration, such as a timer callback or an event handler. Microtasks are promise reactions that run in a checkpoint after each task, draining fully before the loop moves on.

Does this ordering change if the timeout is larger than zero?

No, it makes the gap wider. A longer delay only means the task becomes ready later, while the microtask still runs as soon as the current script finishes.

Every question on one page →·Run your own code →

Embed this figure in your own post →

Drop this into any article or course page. It stays interactive — readers can step through it without leaving your site.