Part 6 · 2 chapters · ~12 min

Runtime

How JavaScript runs on the page: the event loop, tasks and microtasks, long tasks and yielding, the engine's fast paths, memory and leaks, workers, and async.

14

The loop and the engine

How work is scheduled on the main thread and how the engine runs it fast.

Event loop

taskmicrotasksrender?idle

The loop each thread runs: take one task, run it to completion, drain microtasks, maybe render, repeat.

in practiceWhy a long task freezes input and animation; why await does not block the thread.

Task (macrotask)

task: click han…microtasksrendertask: timer76 ms

A unit of work the event loop runs whole: a script, an event dispatch, a timer callback, a message.

in practiceThe grey "Task" bars in the Performance panel; setTimeout and MessageChannel schedule them.

deep dive: Browser P6

Microtask

setTimeout(() => log('task'));Promise.resolve().then(() => log('micro'));log('sync');// sync, micro, task

A callback queued to run right after the current task and before rendering: promise reactions, queueMicrotask, MutationObserver.

in practicePromise chains that starve rendering if they keep queuing more microtasks.

Long task

tasklong task (180 …click waits200 mseverything over 50 ms is blocking time

Any task over 50 ms; while it runs, input waits and frames are not produced.

in practiceRed-cornered tasks in the Performance panel; the main driver of INP and Total Blocking Time.

deep dive: Browser P12 · DevTools P5

Yielding

chunkinputchunkrenderchunk103 ms

Breaking work into chunks and returning to the event loop between them so input and rendering can run.

in practicescheduler.yield(), await on a setTimeout(0), chunked loops; the main fix for long tasks.

scheduler.postTask

user-blockinginput responsesuser-visibledefaultbackgroundanalytics, prefetch

A prioritised task scheduler (user-blocking, user-visible, background) with abort signals, plus scheduler.yield() to continue at the same priority.

in practiceDeferring analytics and prefetch to background priority behind user work.

requestIdleCallback

frame workidle (callback)next frame25.7 ms

Runs a callback when the browser is idle in a frame, with a deadline telling it how much time is left.

in practiceLow-priority work like sending telemetry or warming caches.

deep dive: Browser P6

Call stack

handleClick()validate()parseCart()(task)

The stack of function frames currently executing; the event loop only takes the next task when it is empty.

in practiceStack traces in errors; "Maximum call stack size exceeded" from unbounded recursion.

deep dive: JavaScript P8

Execution context and scope

inner()sees…makeCounter() scopecoun…global

The record of variables, this and the outer scope for a running function; closures keep an outer scope alive after it returns.

in practiceClosures over stale state in React effects; memory held by a closure you forgot.

deep dive: JavaScript P8

Hidden class (shape)

{}shape S0.x = 1S1: x.y = 2S2: x, ysame construction order → same shape

V8's internal description of an object's layout; objects created the same way share one, which keeps property access fast.

in practiceAdding properties in different orders or deleting them makes objects polymorphic and slower.

deep dive: JavaScript P3

Inline cache

monomorphic1 shape: fastestpolymorphic2 to 4 shapesmegamorphic> 4: generic lookup

A per-call-site cache of the shapes seen and where the property lives, turning a lookup into an offset read.

in practiceMonomorphic call sites (one shape) are fast; megamorphic ones (many) fall back to slow lookups.

deep dive: JavaScript P3

JIT (TurboFan, Maglev)

IgnitionbytecodeSparkplugbaselineMaglevmid-tierTurboFanoptimised

Compiling hot functions to machine code using type feedback, and deoptimising when an assumption breaks.

in practiceCode fast after warm-up and slower when types change; --trace-deopt.

deep dive: JavaScript P4
THE RUNTIME LAYER, IN ORDER
how JavaScript runs on the page: the loop, the engine, memory, threads and async
swipe the figure sideways, or tap expand for full screen
1/6
the loop
The loop: one task at a time to completion, microtasks drained after it, rendering when due; events capture down and bubble up within a task.
15

Memory, threads and async

What stays in memory, what runs elsewhere, and how asynchronous work is expressed and cancelled.

Garbage collection

young gen (scavenge)small, frequentcopy survivorsfastold gen (mark-compact)large, rareconcurrent markingcompaction pauses

Automatic reclamation of objects no longer reachable from roots; V8 uses a fast young-generation scavenger and a mostly concurrent old-generation mark-compact.

in practiceGC slices in traces; pauses on allocation-heavy code; memory that does not drop because something still references it.

deep dive: JavaScript P5

Memory leak

t0t+10mt+30mt+60m200 MB heapsawtooth that never comes back down

Memory that stays reachable when the app no longer needs it: detached DOM, forgotten listeners, growing caches, closures held by timers.

in practiceThe Memory panel's heap snapshots and comparison view; a tab that slows down over an hour.

Detached DOM

JS refcache / closureremoved <div>detachedsubtreekept alive

DOM nodes removed from the document but still referenced by JS, so they and their subtrees cannot be collected.

in practiceSearch "Detached" in a heap snapshot; a modal's nodes held by a closure after close.

deep dive: DevTools P6

WeakMap and WeakRef

Mapkey held stronglyleaks if never deletedWeakMapkey held weaklyentry gone with key

References that do not keep their target alive: WeakMap keys and WeakRef targets can be collected when nothing else holds them.

in practiceCaches keyed by DOM nodes or objects without leaking them.

deep dive: JavaScript P12

Web Worker

mainworkerpostMessage(data)heavy workinput stays smoothresult

A separate thread with its own event loop and no DOM, communicating with the page by messages.

in practiceMoving parsing, search indexing, image processing and CRDT merges off the main thread.

deep dive: Browser P7 · FSD P2

Structured clone and transfer

clonedeep copycost ∝ sizeboth keep a copytransfermove ownershipO(1)sender loses access

How postMessage copies data between threads (structured clone) or moves ownership of buffers with zero copy (transferables).

in practiceLarge ArrayBuffers transferred, not copied; functions and DOM nodes cannot be cloned.

deep dive: Browser P7

SharedArrayBuffer and Atomics

mainviewshared bufferAtomics.wait / n…workerview

Memory shared between threads with atomic operations for coordination; requires cross-origin isolation.

in practiceWASM threads, high-performance pipelines; COOP and COEP headers to enable it.

deep dive: Browser P9

Service worker

page fet…service …cache or ne…cachenetwork

A worker that sits between the page and the network for its scope, intercepting fetches, caching, and receiving push.

in practiceOffline apps, cache strategies, the Application panel's Service Workers pane; stale app bugs after deploys.

deep dive: Browser P8 · FSD P8

Promise

pendingfulfilledvaluerejectedreasonsettles exactly once

An object representing a future value, settled once as fulfilled or rejected, with reactions queued as microtasks.

in practiceasync/await is promise sugar; unhandled rejections; Promise.all versus allSettled.

deep dive: JavaScript P10

async/await

// sequential: 2 RTTsconst a = await getA();const b = await getB();const [a, b] = await Promise.all([getA(), getB()]);

Syntax that suspends a function at each await and resumes it as a microtask when the promise settles, without blocking the thread.

in practiceSequential awaits that should be parallel (Promise.all); try/catch for rejections.

deep dive: JavaScript P10

AbortController

uifetchsearch "pa"abort()search "pay"only "pay" results

A signal that cancels fetches, listeners and any API that accepts it, so stale work stops instead of racing.

in practiceCancelling the previous search request on each keystroke; cleanup in React effects.

deep dive: JavaScript P10

Event propagation

windowcapt…documentul (delegated listener)li (target)

Events travel capture (window down to target), target, then bubble (back up); listeners can stop it or prevent the default action.

in practiceEvent delegation on a list; stopPropagation breaking analytics; passive listeners for scroll and touch.

deep dive: Browser P6