Skip to main content

Чому JavaScript вважають однопотоковим

JavaScript називають однопотоковим тому, що в один момент часу у стеку викликів (Call Stack) перебуває і виконується лише один фрагмент коду. Неможливо одночасно щось обчислювати і, наприклад, обробляти подію: нова задача почнеться тільки після завершення попередньої.

Теорія

TL;DR

  • Один потік виконання, один стек викликів, одна задача за раз.
  • Код виконується построково: доки не завершиться поточний рядок, наступний не почнеться.
  • Асинхронні задачі виконуються не всередині JS, а у Web API браузера або в API Node.js.
  • Event Loop лише повертає готові колбеки в стек, коли той звільниться.
  • Плюс однопотоковості: немає гонок даних і складної синхронізації.
  • Мінус: довгі обчислення блокують усе, включно з інтерфейсом.

Швидкий приклад

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

Вивід:

javascript
A B C

JS іде построково: доки не виконається A, B не почнеться. Усе відбувається в одному потоці.

Що це означає технічно

У рушії JavaScript (наприклад, V8) є:

  • Call Stack, стек викликів, тобто код, що виконується зараз;
  • Event Loop, цикл подій, який керує асинхронними задачами;
  • Callback Queue / Microtask Queue, черги колбеків і промісів.

Але все це обслуговує один потік виконання коду.

Асинхронні задачі (мережеві запити, таймери тощо) не виконуються всередині JS: ними займається браузер (Web APIs) або API Node.js, а JS просто отримує сповіщення, коли результат готовий.

Схематично:

text
+----------------------+ | Call Stack | <- виконується рівно один шматок коду +----------------------+ ^ +----------------------+ | Event Loop | <- перевіряє, чи вільний стек +----------------------+ ^ +----------------------+ | Callback / Microtask | <- асинхронні задачі чекають своєї черги +----------------------+

Приклад з асинхронністю

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

Вивід:

javascript
1 3 2

Чому так:

  • JS спершу виконує синхронний код (1, 3);
  • потім, коли стек порожній, Event Loop бере задачу з черги (2).

Навіть із setTimeout(..., 0) JS не робить дві справи одночасно.

Чому це важливо

Однопотоковість означає:

  • простоту коду, немає гонок даних і складної синхронізації;
  • обмеження, довгі операції (важкі цикли, обчислення) блокують усе, і інтерфейс «зависає».
javascript
// Такий цикл заморозить сторінку на кілька секунд: 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 зі спільною пам'яттю: воркер має власний контекст, дані передаються повідомленнями.
  • Забувати, що мікрозадачі (проміси) виконуються раніше за макрозадачі (таймери) після спорожнення стеку.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.