Debug asynchronous ordering
Predicting output from code that mixes synchronous statements, promises, and timers.
JS Puzzles · Const
The event loop coordinates JavaScript's asynchronous work: it runs one macrotask, drains all microtasks, may render, and then continues with the next macrotask. This ordering explains why promise callbacks commonly precede zero-delay timers.
JavaScript repeatedly takes the oldest macrotask, runs it to completion, drains the microtask queue, and then may render before taking another macrotask. Timers, scripts, and user events become macrotasks; promise reactions are microtasks, so they can run before a ready timer.
console.log(1);
setTimeout(() => console.log(2));
Promise.resolve().then(() => console.log(3));
console.log(4);The script task prints 1 and 4 first. After it ends, the promise callback runs as a microtask; only then does the timer callback run as a later macrotask.
Predicting output from code that mixes synchronous statements, promises, and timers.
Breaking expensive work into chunks so input and rendering can proceed.
Choosing a scheduling mechanism based on whether work should run before the next task or in a later task.
The engine takes the oldest macrotask, runs all its synchronous code, then drains every pending microtask before moving to another macrotask. Rendering may occur after that microtask phase.
Follow-up: What happens if a microtask queues another microtask? It is processed during the same drain, before the next macrotask.
A timer callback is a macrotask, while a promise reaction is a microtask. If both become ready during the same turn, the promise reaction runs after the current task and before the timer task.
Follow-up: Does a zero-delay timer run immediately? No; it schedules a later macrotask rather than interrupting the current one.
A long synchronous task prevents the event loop from taking another task. Consequently, user events, timers, and rendering cannot be handled until that task yields.
Follow-up: How can code yield? Split work into smaller tasks, for example by scheduling a later timer, so the event loop can process other work between chunks.
microtasksmacrotaskssetTimeoutPromisetasksrendering