Чому JavaScript вважають однопотоковим
JavaScript називають однопотоковим тому, що в один момент часу у стеку викликів (Call Stack) перебуває і виконується лише один фрагмент коду. Неможливо одночасно щось обчислювати і, наприклад, обробляти подію: нова задача почнеться тільки після завершення попередньої.
Теорія
TL;DR
- Один потік виконання, один стек викликів, одна задача за раз.
- Код виконується построково: доки не завершиться поточний рядок, наступний не почнеться.
- Асинхронні задачі виконуються не всередині JS, а у Web API браузера або в API Node.js.
- Event Loop лише повертає готові колбеки в стек, коли той звільниться.
- Плюс однопотоковості: немає гонок даних і складної синхронізації.
- Мінус: довгі обчислення блокують усе, включно з інтерфейсом.
Швидкий приклад
console.log('A');
console.log('B');
console.log('C');Вивід:
A
B
CJS іде построково: доки не виконається A, B не почнеться. Усе відбувається в одному потоці.
Що це означає технічно
У рушії JavaScript (наприклад, V8) є:
- Call Stack, стек викликів, тобто код, що виконується зараз;
- Event Loop, цикл подій, який керує асинхронними задачами;
- Callback Queue / Microtask Queue, черги колбеків і промісів.
Але все це обслуговує один потік виконання коду.
Асинхронні задачі (мережеві запити, таймери тощо) не виконуються всередині JS: ними займається браузер (Web APIs) або API Node.js, а JS просто отримує сповіщення, коли результат готовий.
Схематично:
+----------------------+
| Call Stack | <- виконується рівно один шматок коду
+----------------------+
^
+----------------------+
| Event Loop | <- перевіряє, чи вільний стек
+----------------------+
^
+----------------------+
| Callback / Microtask | <- асинхронні задачі чекають своєї черги
+----------------------+Приклад з асинхронністю
console.log('1');
setTimeout(() => console.log('2'), 0);
console.log('3');Вивід:
1
3
2Чому так:
- JS спершу виконує синхронний код (
1,3); - потім, коли стек порожній, Event Loop бере задачу з черги (
2).
Навіть із setTimeout(..., 0) JS не робить дві справи одночасно.
Чому це важливо
Однопотоковість означає:
- простоту коду, немає гонок даних і складної синхронізації;
- обмеження, довгі операції (важкі цикли, обчислення) блокують усе, і інтерфейс «зависає».
// Такий цикл заморозить сторінку на кілька секунд:
const start = Date.now();
while (Date.now() - start < 5000) {}
console.log('Only now the UI responds again');А як же багатопотоковість у браузері
Хоча сам JS однопотоковий, браузер і Node.js мають інші потоки поза рушієм JS:
- мережеві запити (XHR,
fetch); - таймери (
setTimeout); - файлова система (у Node.js);
- Web Workers (фоновий потік, але з обмеженнями, без спільного стану).
JS просто отримує результати з цих потоків, але сам виконує код лише в одному.
Підсумок термінів
| Термін | Що означає |
|---|---|
| Однопотоковий | JS виконує код послідовно, в одному потоці |
| Асинхронність | Досягається за рахунок Event Loop і черг |
| Web APIs / Node APIs | Обробляють задачі паралельно, але поза рушієм JS |
| Багатопотоковість | Лише через Web Workers, без доступу до спільних даних |
Типові помилки
- Думати, що
setTimeout(fn, 0)виконає колбек негайно: він чекатиме, доки звільниться стек. - Вважати асинхронність паралельністю: асинхронний код усе одно виконується в тому самому потоці.
- Робити важкі обчислення в основному потоці замість Web Worker і дивуватися, що інтерфейс не реагує.
- Плутати Web Workers зі спільною пам'яттю: воркер має власний контекст, дані передаються повідомленнями.
- Забувати, що мікрозадачі (проміси) виконуються раніше за макрозадачі (таймери) після спорожнення стеку.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.