The Event Loop, Visualized

A promise, alone โ€” interactive event loop example

1console.log('start');
2
3Promise.resolve().then(() => {
4 console.log('from the promise');
5});
6
7console.log('end');

The script begins. console.log("start") runs synchronously on the stack.

1 / 6

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. โ€” A promise, alone ยท step 1 of 6

What is going on here

A promise that is already resolved still does not call your .then callback right away. Promise.resolve() has nothing to wait for, and yet "from the promise" prints after "end". That surprises people, and the reason is worth being precise about.

Resolving a promise does not run its reactions; it queues them. The callback goes onto the microtask queue, and the microtask queue is only drained once the call stack is empty โ€” which means after every remaining line of the current script. The delay has nothing to do with how long the promise took and everything to do with when the loop gets its turn.

This guaranteed asynchrony is deliberate. If a .then callback sometimes ran synchronously and sometimes later depending on whether the promise had already settled, every consumer would have to defend against both orderings. The specification removes the choice: promise reactions are always asynchronous, always at least one microtask away.

Common questions

Why does a resolved promise callback still run last?

Resolving a promise queues its callback as a microtask rather than calling it. Microtasks only run once the call stack is empty, so every remaining line of synchronous code executes first.

Can a .then callback ever run synchronously?

No. The specification guarantees that promise reactions are always deferred to the microtask queue, so the ordering is the same whether the promise settled long ago or a moment before.

What is the microtask queue?

It is the queue that holds promise reactions and queueMicrotask callbacks. It is drained completely after each task, before the next task and before the browser renders.

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.