The Event Loop, Visualized

.then after .then — interactive event loop example

1Promise.resolve()
2 .then(() => {
3 console.log('first');
4 })
5 .then(() => {
6 console.log('second');
7 });
8
9console.log('sync');

The chain is built synchronously, but only the first .then callback is queued now. The second waits — its promise hasn't resolved yet.

1 / 7

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

then #1

Callback queue

Console

Fig. — .then after .then · step 1 of 7

What is going on here

A .then chain is not a single unit of work. Each link is its own microtask, scheduled only when the previous link has produced a value — which is why "sync" prints first, then "first", then "second", instead of the whole chain resolving in one go.

Concretely: the initial promise is already resolved, so the first callback is queued as a microtask. Its return value resolves the intermediate promise created by that first .then, and only that resolution queues the second callback. The links are strictly sequential, each one a separate trip through the queue.

Because a checkpoint drains newly added microtasks too, the whole chain still completes within a single checkpoint — no task can slip in between the links. That is also the mechanism behind the "await costs a microtask tick" rule of thumb, and behind microtask starvation: a chain that keeps extending itself never lets the checkpoint end.

Common questions

Is each .then in a chain a separate microtask?

Yes. Every link is queued only after the previous one returns, so a three-link chain costs three trips through the microtask queue, though all of them drain in the same checkpoint.

Can a setTimeout callback run in the middle of a promise chain?

No. The microtask checkpoint drains to empty, including microtasks queued by earlier microtasks, before the loop selects any task. The whole chain finishes first.

Why does the synchronous log print before any .then callback?

Because .then only registers or queues a callback and returns immediately. The rest of the script keeps running on the same stack, and the queued callbacks wait for that stack to empty.

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.