interviewpath
  • 0day streak
  • 10total XP
  • Level 1
0/5

Unit 2 · Software engineering · Lesson 13 of 15

Browser runtime: event loop, rendering and promises

The browser runs your JavaScript, style calculation, layout and much of painting coordination on one main thread, scheduled by the event loop. Knowing how tasks, microtasks and rendering steps interleave explains frozen interfaces, promise-ordering puzzles and slow interactions, and it is the basis of every frontend performance discussion.
About 3 h 30 minFocus mode

Step 1: Learn the ideas

  • Main thread, tasks and task sources
  • Microtasks: promise reactions, queueMicrotask, MutationObserver
  • Event loop processing model and rendering opportunities
  • requestAnimationFrame and frame budgets
  • Rendering pipeline: style, layout, paint, composite
  • Forced synchronous layout and layout thrashing
  • Long tasks and yielding to the main thread
  • Web Workers for CPU-heavy work

Why it matters

Most frontend performance complaints, such as a button that feels stuck, a frozen spinner or a list that janks while scrolling, trace back to the main thread being busy when the browser needed it. Interviewers use event-loop ordering puzzles to check whether you understand promises precisely, and rendering questions to see whether you can reason about cost instead of guessing.

Core ideas

One thread, several queues. A page's JavaScript, event handlers, style calculation and layout share the main thread. The event loop repeatedly takes one task (a script, a timer callback, an input event, a message), runs it to completion, then performs a microtask checkpoint that drains the microtask queue completely, including microtasks queued by other microtasks. Promise reactions, await continuations, queueMicrotask callbacks and MutationObserver callbacks are microtasks; timers, postMessage and most events are tasks. When the browser decides to render a frame, it runs requestAnimationFrame callbacks and then style, layout and paint.

console.log('1');
setTimeout(() => console.log('5'), 0);
Promise.resolve().then(() => console.log('3'));
(async () => { console.log('2'); await null; console.log('4'); })();
// logs 1 2 3 4 5

Synchronous code finishes first, microtasks run next in the order they were queued, and the timer runs in a later task despite its zero delay. A microtask that keeps re-queuing itself starves rendering and input completely; a chain of timers does not, because the browser regains control between tasks.

The rendering pipeline. After DOM or style changes, the browser recalculates styles, runs layout to compute geometry, paints drawing commands for layers and composites the layers, often on a separate compositor thread using the GPU. Changing geometry (width, position, font size) triggers layout and everything after it; changing only a color skips layout; changing transform or opacity on an element with its own layer can often be handled by the compositor alone, which is why such animations stay smooth while the main thread is busy.

Forced synchronous layout. Reading a layout-dependent value such as offsetHeight or getBoundingClientRect() after a style change forces layout to run immediately. Interleaving writes and reads in a loop (layout thrashing) repeats that work on every iteration; do all reads first and all writes after, or defer the writes to requestAnimationFrame.

Long tasks. A task that occupies the main thread for 50 ms or more counts as a long task, because input arriving during it must wait. At 60 Hz a frame lasts about 16.7 ms, and script, style, layout and paint share that budget. Split work into chunks and yield between them (setTimeout, or scheduler.yield() where supported), do less work per update, or move pure computation to a Web Worker, which has its own thread and event loop but no DOM access.

Common misconceptions

  • "setTimeout(fn, 0) runs right after the current code." It queues a task: pending microtasks, and possibly a rendering step, come first, and browsers enforce a 4 ms minimum once timers are nested deeply enough.
  • "Async code runs in parallel." Promises and async functions interleave on one thread; only workers and the browser's internal threads (network, decoding, compositing) run in parallel with your script.
  • "Microtasks only run at the end of a task." A checkpoint also happens whenever the JavaScript stack empties after a callback, for example between two listeners handling a real user click.
  • "requestAnimationFrame fires every 16 ms." It runs before the next frame the browser renders, follows the display's refresh rate and is paused in most browsers for background tabs; use its timestamp argument rather than assuming an interval.
  • "Any CSS animation is cheap." Animating layout-affecting properties runs layout and paint on the main thread every frame; transforms and opacity are the dependable compositor path.

How to approach it

Learn the processing model once from a precise source, then rely on experiments: record a DevTools Performance trace of a deliberately slow handler, find the long task and fix it. Graduate-level questions are usually ordering puzzles; mid-level and senior frontend interviews describe a janky interaction and ask where the time goes and where the work should run instead.

Suggested order

  1. 1Read the core MDN "JavaScript execution model" page, then the "Event loops" section of the HTML Standard through the processing model, skimming the algorithm steps.
  2. 2Read MDN "Using microtasks in JavaScript with queueMicrotask()" and its in-depth companion; write ten ordering puzzles mixing setTimeout, promises, await, queueMicrotask and requestAnimationFrame, predict each, then run them in the browser console.
  3. 3Read web.dev "Rendering performance" and "Avoid large, complex layouts and layout thrashing"; build a page that interleaves style writes and offsetHeight reads in a loop, record it in the DevTools Performance panel, then batch the reads and compare.
  4. 4Read web.dev "Optimize long tasks"; split a 300 ms synchronous loop into chunks that yield and confirm in a trace that clicks are handled between chunks.
  5. 5Move the same computation into a Web Worker and compare main-thread activity in a new recording.
  6. 6Solve the Promises and Time section of LeetCode 30 Days of JavaScript to rehearse timer and promise semantics.

Step 2: Check off what you can do

Tap each one you can genuinely do or explain. Each earns 10 XP.

Step 3: Go deeper

  • JavaScript execution model (MDN)Start here

    MDN Web Docs

    Agents, the job queue and the event loop as JavaScript defines them; read before predicting promise ordering.

    Open(opens in a new tab)Link responded, 5 Oct 2026
  • HTML Living Standard: Event loops

    WHATWG

    Section: Event loop processing model

    Authoritative order of task selection, microtask checkpoints and the rendering update; skim the algorithm steps.

    Open(opens in a new tab)Link responded, 5 Oct 2026
  • MDN: Using microtasks in JavaScript with queueMicrotask()

    MDN Web Docs

    Tasks versus microtasks with runnable examples, and the in-depth page on execution contexts and event loop types.

    Open(opens in a new tab)Link responded, 5 Oct 2026
  • web.dev: Rendering performanceStart here

    Google Chrome team (web.dev)

    Section: Rendering performance

    The style, layout, paint and composite pipeline; then the layout-thrashing, compositor-only and long-task articles listed under this resource.

    Open(opens in a new tab)Link responded, 5 Oct 2026
  • Chrome DevTools: Analyze runtime performance

    Google (developer.chrome.com)

    Section: Analyze runtime performance

    Recording traces with CPU throttling and reading long tasks and forced reflows in the flame chart.

    Open(opens in a new tab)Link responded, 5 Oct 2026

Step 4: Practice

Use the Promises and Time section of 30 Days of JavaScript (Sleep, Timeout Cancellation, Promise Time Limit, Debounce) for timer and promise semantics. The main practice is hands-on: (1) write ten ordering puzzles, predict each, then run it; (2) build a page whose click handler runs for 300 ms, record it in the DevTools Performance panel with CPU throttling, and fix it by chunking with yields or moving the work to a worker; (3) create layout thrashing on purpose, find the forced-reflow warnings in the trace, and remove them by batching reads before writes.

Record attempts on each problem’s page (+15 XP, +10 more when solved on your own).

Step 5: Test yourself

Answer each one out loud or on paper before you finish the lesson.

  1. Question 1After a task finishes, what runs first: pending microtasks, timers or rendering?
  2. Question 2Why can an endless chain of microtasks freeze a page while an endless chain of setTimeout calls does not?
  3. Question 3What happens to the rest of an async function when it reaches await?
  4. Question 4Which changes trigger layout, which only paint, and which can be composited?
  5. Question 5What is forced synchronous layout, and how do you avoid it?
  6. Question 6What can a Web Worker not do that main-thread code can?

You’re done when you can

  • Predict the output of three unseen event-loop ordering puzzles correctly and explain each step.
  • Explain the style, layout, paint and composite steps and name a property change that skips layout.
  • Find a long task and a forced synchronous layout in a Performance recording of your own page and fix both.
  • Explain when a Web Worker is the right fix and when chunking on the main thread suffices.

Finish the lesson

How confident do you feel?

Lesson complete!

+50 XP

Reviews will come up on the Practice tab so it sticks.

Unlocks next: Frontend state, request races and client-server interfaces, Accessibility and web performance

Your notes and study time

Log study time