Compile Ready
Module 1 · JavaScript Fundamentals

The Single-Threaded Model

Intermediate8m read4m practice12m total
ConcurrencyCall StackBlockingEvent Loop

Introduction

JavaScript runs your code on a single thread with one call stack. Only one statement executes at any instant. Yet apps feel concurrent — they fetch data, animate, and respond to clicks "at the same time".

The resolution to this apparent paradox is non-blocking, asynchronous I/O: the single thread offloads slow work to the host and keeps moving.

Why This Matters

This model explains why a long synchronous loop freezes the whole UI, why setTimeout(fn, 0) still runs after your code, and why CPU-heavy work belongs in a Web Worker. It is one of the most common senior-level discussion topics.

Theory

Single thread, one call stack

Every function call pushes a frame; every return pops one. Because there is only one stack, JavaScript cannot run two functions truly simultaneously. "Run-to-completion" applies: a function runs fully before anything else can.

Blocking is the enemy

If a function takes a long time (a huge loop, synchronous file read, alert), the single thread is blocked — no other code, events, or rendering can happen until it returns. In a browser this looks like a frozen page.

How concurrency is achieved anyway

Slow operations are handed to the host (Web APIs / libuv), which may use its own threads under the hood. When the work finishes, a callback is queued and the event loop runs it once the stack is clear. The JS you write stays single-threaded; the environment provides the concurrency.

True parallelism: Web Workers

For CPU-bound work, browsers offer Web Workers (and Node offers worker threads): separate threads with their own call stack and memory, communicating via message passing. They don't share the main thread's variables, which keeps the model race-free by default.

Visual Diagrams

Run-to-completion on one stack
Time --->
main()  [==================================]
            push a()  [====]
                push b() [==]
            (b returns) pop
            (a returns) pop
(main returns) pop

Nothing else runs until the stack is empty. A long frame blocks everything.

One call stack means one thing at a time; long tasks block the whole app.

Playground

Loading editor…
Console

Press Run to execute the code and see output here.

Output Prediction

Predict the output #1

javascript
1console.log('one');
2setTimeout(() => console.log('two'), 0);
3console.log('three');

Coding Exercises

Don't block the thread: chunk the work

Medium

You must sum the numbers 0..N where N is very large, but you must not freeze the UI. Write a function sumInChunks(n, chunkSize, done) that processes the sum in small chunks, yielding to the event loop between chunks, and calls done(total) when finished.

Interview Questions

1If JavaScript is single-threaded, how can it do things concurrently?

The JavaScript code runs on one thread with one call stack, but the environment provides concurrency. Slow operations (timers, network, file I/O) are delegated to host Web APIs/libuv, which may use OS threads. When they complete, a callback is queued and the event loop runs it once the stack is empty. So concurrency comes from non-blocking I/O and the event loop, not from multiple JS threads.

Asked at:AmazonMetaMicrosoft

Follow-ups

  • What blocks the main thread and how do you avoid it?
  • When would you reach for a Web Worker?
2What does it mean that JavaScript has 'run-to-completion' semantics?

Once a function starts, it runs fully before any other queued task or callback can execute — the event loop won't interrupt it. This makes reasoning about state easier (no pre-emption mid-function) but means a long-running function blocks everything, including rendering and events.

Quiz

1. Why does a long synchronous for-loop freeze the browser UI?

2. Where should heavy CPU-bound work go to keep the UI responsive?

Summary

  • JavaScript executes your code on one thread with a single call stack.
  • Run-to-completion: a function finishes before anything else runs.
  • Long synchronous work blocks events and rendering — keep tasks short.
  • Concurrency comes from non-blocking host APIs + the event loop; parallelism from Web Workers.

Cheat Sheet

Model: one thread, one call stack, run-to-completion.

Blocking: long sync work freezes UI (no events, no repaint).

Concurrency: host APIs do slow work off-thread → queue callbacks → event loop.

Parallelism: Web Workers / worker threads (own stack + memory, message passing).