logo

Event Loop Visualizer

Free online JavaScript event loop visualizer. Step through a preset script and watch the call stack, microtask queue, and macrotask queue advance, with console output printed in order — the clearest way to see why Promise callbacks run before setTimeout.
logo
Paji Dev Workshop
Event Loop Visualizer

Event Loop Visualizer

Free online JavaScript event loop visualizer. Step through a preset script and watch the call stack, microtask queue, and macrotask queue advance, with console output printed in order — the clearest way to see why Promise callbacks run before setTimeout.
Processing

About the event loop

What the event loop does

JavaScript runs on a single thread, so it can only do one thing at a time. Synchronous code runs top to bottom on the call stack. Anything asynchronous — a setTimeout callback, a Promise .then, a click handler — does not run inline; it is placed in a queue. The event loop is the simple rule that decides what runs next: whenever the call stack is empty, it takes the next waiting task and runs it, then repeats. This tool makes that loop visible one step at a time.

Microtasks vs macrotasks

There are actually two queues, and their priority is the key to the whole puzzle. Promise callbacks (and queueMicrotask) go into the microtask queue; setTimeout, setInterval, and most events go into the macrotask queue. The rule: after each macrotask (and after the initial script), the loop drains the entire microtask queue before it runs the next macrotask. That is why a Promise .then always beats a setTimeout with the same delay — the microtask is emptied first. Watch the two queues in this tool and the ordering stops being a mystery.

FAQ

Common questions and answers about this topic.

Why does a Promise callback run before setTimeout?

Because Promise callbacks are microtasks and setTimeout callbacks are macrotasks, and the event loop drains the entire microtask queue after every macrotask (and after the initial script) before it runs the next macrotask. So even a setTimeout with 0ms waits in the macrotask queue while every pending microtask runs first.

What counts as a microtask?

Promise reactions (.then, .catch, .finally), await continuations, queueMicrotask callbacks, and MutationObserver callbacks are microtasks. setTimeout, setInterval, requestAnimationFrame, I/O, and most DOM events are macrotasks. The distinction matters because the loop empties all microtasks between macrotasks, so microtasks can starve rendering or later timers if you schedule them endlessly.

Does setTimeout with 0ms run immediately?

No. A 0ms delay only means the callback is eligible to run as soon as possible, but it still goes to the macrotask queue and waits until the current synchronous code and all pending microtasks have finished. Browsers also clamp nested timeouts to a minimum (about 4ms after several levels), so 0ms is a lower bound, not an instant call.