JavaScript is single-threaded. Synchronous code runs to completion on the call stack. Asynchronous work is scheduled onto queues. The event loop decides what runs next when the stack is empty.
Microtasks vs macrotasks
Microtasks include Promise reactions (.then, queueMicrotask). Macrotasks include setTimeout, setInterval, and many I/O callbacks.
After each turn of synchronous work, the engine drains all queued microtasks before taking the next macrotask. That is why this prints Promise logs before the timer:
console.log("Start");
setTimeout(() => console.log("timeout"), 0);
Promise.resolve().then(() => console.log("then"));
console.log("End");
// Start → End → then → timeoutSee it visually
Use the JavaScript Execution Visualizer to step through call stack, microtask queue, and timer queue. The default demo matches Node's console order for chained .then callbacks.
FAQ
Do microtasks always run before setTimeout?
Yes for the classic case: after the current call stack clears, the engine drains the microtask queue completely before the next macrotask (including setTimeout 0).
Are Promise.then callbacks microtasks?
Yes. Promise reactions are queued as microtasks. That is why they often log before a zero-delay timer.