Skip to main content

Why JavaScript is considered single threaded

JavaScript is called single threaded because at any moment only one piece of code sits in the call stack and runs. You cannot compute something and, say, handle an event at the same time: a new task starts only after the previous one has finished.

Theory

TL;DR

  • One thread of execution, one call stack, one task at a time.
  • Code runs line by line: until the current line finishes, the next one does not start.
  • Asynchronous tasks run outside JS, in the browser Web APIs or in the Node.js API.
  • The Event Loop only returns finished callbacks to the stack once it is free.
  • The upside of one thread: no data races and no complex synchronisation.
  • The downside: long computations block everything, including the user interface.

Quick example

javascript
console.log('A'); console.log('B'); console.log('C');

Output:

javascript
A B C

JS goes line by line: until A has run, B does not start. Everything happens in one thread.

What this means technically

A JavaScript engine (for example V8) has:

  • a call stack, the code being executed right now;
  • an Event Loop that drives asynchronous tasks;
  • a callback queue and a microtask queue for callbacks and promises.

But all of this serves one thread of code execution.

Asynchronous tasks (network requests, timers and so on) do not run inside JS: they are handled by the browser (Web APIs) or by the Node.js API, and JS simply gets a notification when the result is ready.

Schematically:

text
+----------------------+ | Call Stack | <- exactly one piece of code is running +----------------------+ ^ +----------------------+ | Event Loop | <- checks whether the stack is free +----------------------+ ^ +----------------------+ | Callback / Microtask | <- asynchronous tasks wait their turn +----------------------+

An example with asynchrony

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

Output:

javascript
1 3 2

Why:

  • JS first runs the synchronous code (1, 3);
  • then, once the stack is empty, the Event Loop takes a task from the queue (2).

Even with setTimeout(..., 0), JS does not do two things at once.

Why this matters

Being single threaded means:

  • simple code, there are no data races and no complex synchronisation;
  • a limitation, long operations (heavy loops, computations) block everything, and the interface «freezes».
javascript
// A loop like this freezes the page for several seconds: const start = Date.now(); while (Date.now() - start < 5000) {} console.log('Only now the UI responds again');

What about multithreading in the browser

Although JS itself is single threaded, the browser and Node.js have other threads outside the JS engine:

  • network requests (XHR, fetch);
  • timers (setTimeout);
  • the file system (in Node.js);
  • Web Workers (a background thread, but with restrictions, no shared state).

JS simply receives results from those threads, while running code in only one.

Summary of terms

TermWhat it means
Single threadedJS runs code sequentially, in one thread
AsynchronyAchieved through the Event Loop and the queues
Web APIs / Node APIsProcess tasks in parallel, but outside the JS engine
MultithreadingOnly through Web Workers, with no access to shared data

Common mistakes

  • Thinking that setTimeout(fn, 0) runs the callback immediately: it waits until the stack is free.
  • Treating asynchrony as parallelism: asynchronous code still runs in the same thread.
  • Doing heavy computation on the main thread instead of in a Web Worker, then wondering why the interface stops responding.
  • Confusing Web Workers with shared memory: a worker has its own context and data is passed as messages.
  • Forgetting that microtasks (promises) run before macrotasks (timers) once the stack is empty.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.