Useful Cheatsheetsusefulcheatsheets.com
JavaScript Event Loop
Chapter 29 · Page 229
Advanced

JavaScript Event Loop

Learn how the call stack, microtask queue, and task queue control JavaScript execution order.

TL;DR

  1. 01The call stack executes synchronous code one frame at a time.
  2. 02Microtasks execute immediately after the current stack frame completes.
  3. 03Promises resolve before macrotasks like setTimeout callbacks get processed.

Tips

  1. 01Use the native queueMicrotask() method to run scripts immediately after synchronous blocks but before paint updates.
  2. 02Split heavy data processing into smaller setTimeout batches to give browsers time to render frames.

Warnings

  1. 01Running a long synchronous loop blocks the single execution thread, freezing page interactions and rendering updates.
  2. 02Remember that recursive promise microtasks can starve macrotasks by preventing the event loop from advancing.
Notes
Useful Cheatsheetsusefulcheatsheets.com
JavaScript Event Loop
Chapter 29 · Page 230
Advanced

JavaScript Event Loop

(continued)

The Call Stack

  • Call stack frames

    Tracks function calls in progress, stacking execution frames until returns occur.

    function a() { b(); }
    function b() { console.log("b"); }
    a(); // pushes a, then b, then pops
  • Single execution thread

    Allows only one stack block to run at any single time.

    // Synchronous execution runs sequentially
  • Sync completeness

    Processes synchronous scripts completely before checking background event queues.

    console.log(1);
    console.log(2);
    // 1 always logs before 2
  • Stack overflow

    Triggers errors when unbounded recursive calls consume all stack memory.

    function recurse() { return recurse(); }
    // recurse(); // Maximum stack exceeded
Notes
Useful Cheatsheetsusefulcheatsheets.com
JavaScript Event Loop
Chapter 29 · Page 231
Advanced

JavaScript Event Loop

(continued)

Macrotasks and the Task Queue

  • Macrotask callbacks

    Includes timer callbacks, network input, and user action triggers.

    setTimeout(() => console.log("macro"), 0);
    console.log("sync");
  • Single task turns

    Executes exactly one macrotask per event loop rotation iteration.

    setTimeout(() => console.log("1"), 0);
    setTimeout(() => console.log("2"), 0);
    // separate event loop iterations
  • Browser painting

    Interleaves browser paint updates between subsequent task queue turns.

    // Repaints occur after a macrotask completes
Notes
Useful Cheatsheetsusefulcheatsheets.com
JavaScript Event Loop
Chapter 29 · Page 232
Advanced

JavaScript Event Loop

(continued)

Microtasks and Promises

  • Promise microtasks

    Runs resolve, reject, and finally callbacks inside the microtask queue.

    Promise.resolve().then(() => console.log("micro"));
  • Direct queueing

    Schedules low-level microtasks directly using the queueMicrotask method.

    queueMicrotask(() => console.log("fast"));
  • Full queue draining

    Forces 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-back
  • Nested microtasks

    Processes nested microtasks inside the same loop iteration drain phase.

    Promise.resolve().then(() => {
      Promise.resolve().then(() => console.log("nested"));
    });
Notes
Useful Cheatsheetsusefulcheatsheets.com
JavaScript Event Loop
Chapter 29 · Page 233
Advanced

JavaScript Event Loop

(continued)

setTimeout vs Promise Ordering

  • Order priority

    Executes promise resolutions before timers scheduled within the same block.

    setTimeout(() => console.log("time"), 0);
    Promise.resolve().then(() => console.log("prom"));
    // logs: prom, time
  • Loop interleaving

    Runs 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, time
  • rAF visual timing

    Runs animation callbacks before paint updates, outside standard queues.

    requestAnimationFrame(() => console.log("paint"));
Notes
Useful Cheatsheetsusefulcheatsheets.com
JavaScript Event Loop
Chapter 29 · Page 234
Advanced

JavaScript Event Loop

(continued)

UI Responsiveness

  • Execution chunking

    Schedules 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 Workers

    Delegates 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);
Notes
Useful Cheatsheetsusefulcheatsheets.com
JavaScript Event Loop
Chapter 29 · Page 235
Advanced

JavaScript Event Loop

(FAQ)

FAQ

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.

Useful Cheatsheetsusefulcheatsheets.com
JavaScript Event Loop
Chapter 29 · Page 236
Advanced

JavaScript Event Loop

(In Practice)
In Practice

Predicting Execution Order

Combines synchronous execution, Promise microtasks, a setTimeout timer, and async awaits to demonstrate execution priorities.

  1. 01Execute the synchronous start log statement immediately.
  2. 02Register a zero-delay timeout callback into the macrotask queue.
  3. 03Push a Promise resolution callback to the microtask queue.
  4. 04Run the async function synchronously up to the first await keyword.
  5. 05Drain the complete microtask queue before picking up the pending macrotask.
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, 2
Takeaway

Synchronous execution completes first, then the microtask queue drains fully, and finally the next macrotask runs.