What gets logged 2?
Prompt
What gets logged when the following code is executed?
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');Remember that setTimeout(..., 0) does NOT mean "run
immediately." It means "run as soon as you can, but only
after the current synchronous code finishes and after any
microtasks." The 0 is a minimum delay, not a guarantee.
Promise .then() callbacks are microtasks.
setTimeout callbacks are macrotasks. The event loop
always finishes all microtasks before it touches a single
macrotask.
Solution
The output is: 1, 4, 3, 2.
Explanation
This is a simpler version of the classic event loop question, but it still catches a lot of people off guard. The instinct is to think setTimeout(..., 0) means "run this right now, with zero delay." If that were true, you'd expect 2 to appear right after 1. But that's not how it works.
Walking through it
The JavaScript engine runs all synchronous code first, top to bottom, without stopping.
console.log('1') runs immediately. Logs "1".
setTimeout(() => console.log('2'), 0) doesn't run the callback. It hands it off to the browser's timer mechanism and says "call me back as soon as possible." The browser puts this callback into the macrotask queue. Even with a delay of 0, it has to wait its turn.
Promise.resolve().then(() => console.log('3')) creates an already-resolved Promise. The .then() callback gets placed into the microtask queue. It won't run now, but it's in a higher-priority line than the setTimeout callback.
console.log('4') runs immediately. Logs "4".
At this point, all synchronous code is done. The output so far is 1, 4.
What happens next
The event loop now checks: "Are there any microtasks waiting?" Yes, there's the Promise .then() callback. It runs that. Logs "3".
Only after the microtask queue is completely empty does the event loop look at the macrotask queue. It finds the setTimeout callback. Logs "2".
Final output: 1, 4, 3, 2.
Why setTimeout(..., 0) is never truly instant
This trips up a lot of developers. The 0 in setTimeout doesn't mean "zero delay." It means "the minimum possible delay." The callback still has to go through the full event loop cycle: wait for all synchronous code to finish, wait for all microtasks to drain, and then it gets to run. In practice, browsers also enforce a minimum delay of about 4ms for nested setTimeout calls, so it's never truly instant even if the event loop were empty.
This is actually useful to know beyond interviews. If you ever need to "defer" work to the next macrotask (maybe to let the browser repaint first), setTimeout(..., 0) is the classic trick. And if you need something to run sooner than setTimeout but still asynchronously, queueMicrotask() or Promise.resolve().then() will run at the microtask level, which fires before the next macrotask.
The priority ladder
It helps to remember the three priority levels:
- Synchronous code runs first, always. No exceptions.
- Microtasks (Promise callbacks,
queueMicrotask,MutationObserver) run next. The entire microtask queue drains before anything else. - Macrotasks (
setTimeout,setInterval, I/O callbacks) run last, one at a time.
If you keep this ladder in your head, you can answer any "what gets logged" question about event loop ordering.