setTimeout(fn, 0)
setTimeout(fn, 0) does not run the function instantly: it simply puts the callback fn into the macrotask queue so that it runs after the current code and all microtasks have finished. A zero delay means "on the next Event Loop iteration", not "right now".
Theory
TL;DR
setTimeout(fn, 0)queuesfnas a macrotask instead of running it immediately.- The callback waits for the call stack to become free, because the Event Loop never interrupts code that is already running.
- Every microtask, such as
Promise.thenandqueueMicrotask, runs before it. - The order is always: the call stack, then microtasks, then
setTimeout. - It is the standard way to defer work, to break up a long synchronous block and to let the interface update.
- The call returns a timer id, which you can cancel with
clearTimeout.
Quick example
console.log('A');
setTimeout(() => console.log('B'), 0);
console.log('C');Output:
A
C
BWhy not A, then B, then C? Because a timer callback is not allowed to cut into code that is already running.
What happens step by step
console.log('A')runs right away, on the call stack.setTimeout(() => console.log('B'), 0)hands the task to the Web API, as a timer with a0ms delay.- The timer "fires" instantly and the callback is added to the macrotask queue.
- JavaScript keeps running the current stack, so it prints
C. - Once the call stack is empty, the Event Loop takes
() => console.log('B')from the queue and runs it.
Why even 0 does not run immediately
Because everything in JavaScript goes through the Event Loop, and it never interrupts the current call stack.
Even with a delay of
0, the callback still has to wait until the stack is free.
So setTimeout(fn, 0) is a way to defer a function to the next Event Loop iteration.
One more detail worth remembering: by specification browsers apply a minimum threshold of about 4 ms for nested timers, so the real delay is almost never zero.
Example with a promise (microtask)
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
console.log('end');The order:
end
promise
timeoutExplanation:
Promise.then()creates a microtask;setTimeout()creates a macrotask;- and microtasks always run before macrotasks.
Practical uses
-
Defer execution so the main code is not blocked:
javascriptsetTimeout(() => { console.log('Runs after the main code'); }, 0); -
Break a synchronous loop so the interface has time to update:
javascriptfor (let i = 0; i < 1e9; i++) {} // heavy operation console.log('The interface is frozen...'); setTimeout(() => { console.log('Now we run after the pause'); }, 0); -
Reset the stack, for example in recursive computations:
javascriptfunction nextStep() { setTimeout(nextStep, 0); // every repetition is a new Event Loop iteration } nextStep();
Summary
| Property | Value |
|---|---|
| What it does | Queues the callback as a macrotask |
| When it runs | After the current stack and all microtasks |
| A delay of "0" | Not instantly, but on the next Event Loop cycle |
| Queue | Macrotasks |
| Returns | A timer id, cancellable with clearTimeout |
Visually it looks like this:
[Call Stack] -> the current code runs here
[Microtask Queue] -> promises and queueMicrotask
[Task Queue] -> setTimeout(fn, 0)setTimeout(fn, 0) always waits until:
- the call stack is cleared,
- the microtasks have run,
- and only then does
fnrun.
Common mistakes
- Reading
0as "immediately". It means "as soon as possible once the stack is free", and the difference matters a lot when heavy synchronous code is nearby. - Treating
setTimeout(fn, 0)andPromise.resolve().then(fn)as equivalent. A promise is a microtask and will always beat the timer. - Counting on exactly 0 ms. Browsers clamp nested timers to roughly 4 ms, and in a background tab the delays grow much larger.
- Curing a frozen interface with a zero timer. It only gives the browser a chance to repaint between chunks of work; if the work itself is heavy, split it into pieces or move it to a Web Worker.
- Forgetting
clearTimeout. A deferred callback can fire after the component has already left the screen and then touch stale data.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.