The Event Loop, Visualized

The checkpoint runs between every task โ€” interactive event loop example

1setTimeout(() => {
2 console.log('task 1');
3 Promise.resolve().then(() => console.log('micro 1'));
4}, 0);
5
6setTimeout(() => {
7 console.log('task 2');
8 Promise.resolve().then(() => console.log('micro 2'));
9}, 0);

The first setTimeout registers its callback with a 0ms timer. Nothing runs yet.

1 / 10

Call stack

script

Web APIs โ€” browser land

task 1 cb 0ms

The event loop โ€” is the call stack empty? โ†’ drain the microtask queue first โ†’ then take one callback.

Microtask queue โ€” priority

Callback queue

Console

Fig. โ€” The checkpoint runs between every task ยท step 1 of 10

What is going on here

Most people learn "microtasks run before tasks" from the classic promise-vs-setTimeout example and quietly generalise it into "all microtasks run, then all tasks". This scene breaks that. The microtask queue is drained after every single task, not once at the end of the script.

So the loop's real rhythm is: run one task, drain the microtask queue completely, run one task, drain again. A microtask created inside task 1 runs before task 2 starts, even though task 2 was queued much earlier. That is why the output interleaves rather than grouping.

This matters in practice whenever a task schedules promise work. A click handler that resolves a promise gets its continuation before the next queued click. And it is the mechanism behind microtask starvation: if a microtask keeps queueing another microtask, the checkpoint never ends, and the next task โ€” every timer, every event, every repaint โ€” waits forever.

Common questions

Do all microtasks run before all macrotasks?

No. The microtask queue is drained after each individual task, so a microtask queued during one task runs before the next task, but microtasks queued later still wait for their own checkpoint. The pattern is task, drain, task, drain.

When exactly does the microtask checkpoint run?

Whenever the JavaScript execution stack empties after a task completes. In practice that is after the script, after each timer or event callback, and before the browser proceeds to rendering.

Can a microtask delay a setTimeout callback?

Yes. A queued timer callback cannot run until the current checkpoint finishes, and because a microtask can queue more microtasks, an unbounded chain can postpone timers indefinitely.

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.