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
console.log('A');
console.log('B');
console.log('C');Output:
A
B
CJS 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:
+----------------------+
| 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
console.log('1');
setTimeout(() => console.log('2'), 0);
console.log('3');Output:
1
3
2Why:
- 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».
// 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
| Term | What it means |
|---|---|
| Single threaded | JS runs code sequentially, in one thread |
| Asynchrony | Achieved through the Event Loop and the queues |
| Web APIs / Node APIs | Process tasks in parallel, but outside the JS engine |
| Multithreading | Only 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 readyA concise answer to help you respond confidently on this topic during an interview.