Чому Node.js вважають однопоточним (single-threaded)?
Що означає «однопоточний» (single-threaded)
Коли кажуть, що Node.js однопоточний, мають на увазі, що:
Основний потік виконання JavaScript-коду в Node.js - один. Саме в цьому потоці працює event loop, який обробляє всі callback-и, промиси та події.
Тобто:
- У Node.js немає безлічі потоків, як у Java, C++ чи Python (за замовчуванням).
- Ваш JS-код виконується в одному основному потоці, послідовно.
Як це працює всередині
Node.js побудований на рушії V8 (виконує JS) та бібліотеці libuv (керує асинхронністю й потоками).
Схематично:
┌───────────────────────────────┐
│ Ваш JavaScript-код (1 потік)│ ← Event Loop
└───────────────────────────────┘
↓
┌───────────────────┐
│ libuv ThreadPool│ ← фонова робота (I/O)
└───────────────────┘Основна ідея:
- JS-код працює в одному потоці.
- Коли ви викликаєте асинхронну функцію (наприклад,
fs.readFile), Node.js делегує важку операцію фоновому пулу потоків (Thread Pool), а не блокує основний потік. - Коли операція завершується, її результат повертається назад в event loop, і викликається callback або promise.
Приклад
const fs = require('fs');
console.log('Початок');
fs.readFile('data.txt', 'utf8', (err, data) => {
console.log('Файл прочитано');
});
console.log('Кінець');Порядок виводу:
Початок
Кінець
Файл прочитаноЧому саме так?
- JS виконується в одному потоці.
fs.readFileйде у фоновий потік libuv.- Основний потік не чекає і продовжує виконання.
Але у Node.js є й інші потоки
Так, усередині Node.js є додаткові потоки, але вони не виконують JavaScript напряму.
| Компонент | Потоки | Що робить |
|---|---|---|
| Event Loop | 1 | Виконує JS-код, обробляє події |
| libuv Thread Pool | 4 за замовчуванням | Виконує операції I/O (файли, DNS, крипто) |
| Системні потоки | багато | ОС, мережеві операції, ядро |
Кількість потоків у пулі можна змінити:
UV_THREADPOOL_SIZE=8 node app.jsЧому це важливо
Node.js не використовує багатопоточність для виконання JS-коду, але використовує додаткові потоки для фонових операцій.
Тож:
- Він однопоточний з точки зору виконання JavaScript.
- Але багатопоточний під капотом - для введення-виведення й системних задач.
Чому це зручно
- Проста модель конкурентності Немає проблем із синхронізацією, м'ютексами та блокуваннями.
- Асинхронність замість потоків Замість безлічі потоків використовується event loop і non-blocking I/O.
- Масштабованість Завдяки неблокуючій архітектурі Node.js обробляє тисячі запитів на одному потоці.
Але можна використовувати багатопоточність
Якщо потрібно розподілити обчислення по ядрах, Node.js пропонує:
| Підхід | Модуль | Опис |
|---|---|---|
| Cluster API | cluster | Запускає кілька процесів Node.js (за кількістю ядер CPU) |
| Worker Threads | worker_threads | Дозволяє запускати JS-код в окремих потоках |
| child_process | fork, spawn, exec | Запускає зовнішні процеси |
Приклад із Worker Threads:
const { Worker } = require('worker_threads');
new Worker('./worker.js'); // окремий потік для важких обчисленьПідсумок
| Характеристика | Node.js |
|---|---|
| Потік для JS-коду | Один (event loop) |
| Потоки під капотом | Так (у libuv) |
| Виконання JS паралельно | Ні |
| Асинхронність | Через event loop |
| Багатопоточність доступна вручну | Так (через worker_threads, cluster) |
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.