Part VI — Advanced JS: concurrency and multi-threaded browser apps
37. Why JS is (mostly) single-threaded, and what that really means
The precise claim is: each JS realm (roughly: each tab, or each worker) has exactly one thread executing its JS, not "the browser has one thread total." A page with 3 Web Workers genuinely has 4 independent JS threads running concurrently — the main thread and each worker's own thread, each with its own call stack, its own event loop, and (critically) no shared memory by default — communication only happens via message-passing (§38).
This design was a deliberate trade-off: shared-memory multithreading (as in C/Java) requires locks, and locks in a UI thread that also handles user input and rendering are a well-known source of deadlocks and freezes. JS's single-threaded-per-realm model makes an entire class of concurrency bugs (data races on shared objects) structurally impossible on the main thread — at the cost of needing explicit APIs (Workers) to use multiple CPU cores at all.
Main thread realm: [event loop] -- [call stack] -- DOM access, UI events
Worker 1 realm: [event loop] -- [call stack] -- NO DOM access, own globals
Worker 2 realm: [event loop] -- [call stack] -- NO DOM access, own globals
^ each is single-threaded internally; the THREE run in parallel