Lesson 4 of 80
How single-threaded JavaScript does many things at once — the call stack, the queues, and why setTimeout(0) still waits.
16 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 2
What does setTimeout(fn, 0) actually promise about when fn runs?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
JavaScript runs on one thread — it can only do one thing at a time. So how does it handle timers, clicks, and network calls without freezing?
It parks them. When something async finishes, its callback waits in a queue. The event loop is the doorman: whenever the main call stack is empty, it lets the next waiting callback in.
There are two lines, and one has priority:
.then, await continuations). Highest priority.setTimeout), I/O, UI events.The rule: after each macrotask, JavaScript drains the entire microtask queue before touching the next macrotask.
console.log("1");
setTimeout(() => console.log("2 (timer)"), 0);
Promise.resolve().then(() => console.log("3 (promise)"));
console.log("4");
// order: 1, 4, 3 (promise), 2 (timer)
Even though the timer is set to 0, the promise runs first — microtasks jump the queue.
Your code runs on the call stack. Along the way it schedules two things.
async/await is promises underneath. The code after an await is a microtask continuation — not magic threading.setTimeout delay is a minimum, not a guarantee. The callback runs no sooner than the delay, once the loop gets to it."What logs first — a setTimeout(0) or a resolved Promise.then?" is a rite of passage. Getting it right, and explaining microtasks vs macrotasks, signals you actually understand async, not just await.