Learn how the call stack, microtask queue, and task queue control JavaScript execution order.
setTimeout callbacks get processed.queueMicrotask() method to run scripts immediately after synchronous blocks but before paint updates.setTimeout batches to give browsers time to render frames.Call stack framesTracks function calls in progress, stacking execution frames until returns occur.
function a() { b(); }
function b() { console.log("b"); }
a(); // pushes a, then b, then popsSingle execution threadAllows only one stack block to run at any single time.
// Synchronous execution runs sequentiallySync completenessProcesses synchronous scripts completely before checking background event queues.
console.log(1);
console.log(2);
// 1 always logs before 2Stack overflowTriggers errors when unbounded recursive calls consume all stack memory.
function recurse() { return recurse(); }
// recurse(); // Maximum stack exceededMacrotask callbacksIncludes timer callbacks, network input, and user action triggers.
setTimeout(() => console.log("macro"), 0);
console.log("sync");Single task turnsExecutes exactly one macrotask per event loop rotation iteration.
setTimeout(() => console.log("1"), 0);
setTimeout(() => console.log("2"), 0);
// separate event loop iterationsBrowser paintingInterleaves browser paint updates between subsequent task queue turns.
// Repaints occur after a macrotask completesPromise microtasksRuns resolve, reject, and finally callbacks inside the microtask queue.
Promise.resolve().then(() => console.log("micro"));Direct queueingSchedules low-level microtasks directly using the queueMicrotask method.
queueMicrotask(() => console.log("fast"));Full queue drainingForces all queued microtasks to run before the loop advances.
Promise.resolve().then(() => console.log("m1"));
Promise.resolve().then(() => console.log("m2"));
// m1 and m2 execute back-to-backNested microtasksProcesses nested microtasks inside the same loop iteration drain phase.
Promise.resolve().then(() => {
Promise.resolve().then(() => console.log("nested"));
});Order priorityExecutes promise resolutions before timers scheduled within the same block.
setTimeout(() => console.log("time"), 0);
Promise.resolve().then(() => console.log("prom"));
// logs: prom, timeLoop interleavingRuns all pending microtasks before processing any scheduled timeout callbacks.
setTimeout(() => console.log("time"), 0);
[1, 2].forEach(n => {
Promise.resolve().then(() => console.log(n));
});
// logs: 1, 2, timerAF visual timingRuns animation callbacks before paint updates, outside standard queues.
requestAnimationFrame(() => console.log("paint"));Execution chunkingSchedules iteration batches with setTimeout to avoid locking layout repaints.
function chunk(items, index = 0) {
const end = Math.min(index + 100, items.length);
for (let i = index; i < end; i++) { /* do work */ }
if (end < items.length) {
setTimeout(() => chunk(items, end), 0);
}
}Web WorkersDelegates heavy computations to separate threads away from main render loops.
const worker = new Worker("task.js");
worker.postMessage(data);
worker.onmessage = e => console.log(e.data);The event loop orchestrates asynchronous callbacks inside a single-threaded runtime. It polls task queues when the call stack clears and forwards pending operations to the execution thread.
Promises resolve inside the microtask queue, which has higher execution priority. The event loop drains all microtasks before picking up the next macrotask from the timer queue.
Microtasks handle Promise resolutions and direct queueMicrotask() actions. Macrotasks process callbacks from timers, user interactions, and fetch operations. Microtasks drain completely between each individual macrotask.
JavaScript runs on a single main thread. Long calculations occupy the call stack, preventing the event loop from rendering layout repaints or handling clicks. Offload heavy math to Web Workers.
No, a zero delay registers a callback into the macrotask queue. The engine must finish executing the current call stack and empty the microtask queue before starting it.
Predicting Execution Order
Combines synchronous execution, Promise microtasks, a setTimeout timer, and async awaits to demonstrate execution priorities.
console.log("1: sync start");
setTimeout(() => {
console.log("2: setTimeout (macrotask)");
}, 0);
Promise.resolve().then(() => {
console.log("3: promise (microtask)");
});
async function run() {
console.log("4: async start (sync)");
await null;
console.log("5: async microtask");
}
run();
console.log("6: sync end");
// Output order: 1, 4, 6, 3, 5, 2Synchronous execution completes first, then the microtask queue drains fully, and finally the next macrotask runs.