Skip to main content

Чому 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
┌───────────────────────────────┐ │ Ваш JavaScript-код (1 потік)│ ← Event Loop └───────────────────────────────┘ ┌───────────────────┐ │ libuv ThreadPool│ ← фонова робота (I/O) └───────────────────┘

Основна ідея:

  • JS-код працює в одному потоці.
  • Коли ви викликаєте асинхронну функцію (наприклад, fs.readFile), Node.js делегує важку операцію фоновому пулу потоків (Thread Pool), а не блокує основний потік.
  • Коли операція завершується, її результат повертається назад в event loop, і викликається callback або promise.

Приклад

javascript
const fs = require('fs'); console.log('Початок'); fs.readFile('data.txt', 'utf8', (err, data) => { console.log('Файл прочитано'); }); console.log('Кінець');

Порядок виводу:

javascript
Початок Кінець Файл прочитано

Чому саме так?

  • JS виконується в одному потоці.
  • fs.readFile йде у фоновий потік libuv.
  • Основний потік не чекає і продовжує виконання.

Але у Node.js є й інші потоки

Так, усередині Node.js є додаткові потоки, але вони не виконують JavaScript напряму.

КомпонентПотокиЩо робить
Event Loop1Виконує JS-код, обробляє події
libuv Thread Pool4 за замовчуваннямВиконує операції I/O (файли, DNS, крипто)
Системні потокибагатоОС, мережеві операції, ядро

Кількість потоків у пулі можна змінити:

javascript
UV_THREADPOOL_SIZE=8 node app.js

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

Node.js не використовує багатопоточність для виконання JS-коду, але використовує додаткові потоки для фонових операцій.

Тож:

  • Він однопоточний з точки зору виконання JavaScript.
  • Але багатопоточний під капотом - для введення-виведення й системних задач.

Чому це зручно

  1. Проста модель конкурентності Немає проблем із синхронізацією, м'ютексами та блокуваннями.
  2. Асинхронність замість потоків Замість безлічі потоків використовується event loop і non-blocking I/O.
  3. Масштабованість Завдяки неблокуючій архітектурі Node.js обробляє тисячі запитів на одному потоці.

Але можна використовувати багатопоточність

Якщо потрібно розподілити обчислення по ядрах, Node.js пропонує:

ПідхідМодульОпис
Cluster APIclusterЗапускає кілька процесів Node.js (за кількістю ядер CPU)
Worker Threadsworker_threadsДозволяє запускати JS-код в окремих потоках
child_processfork, spawn, execЗапускає зовнішні процеси

Приклад із Worker Threads:

javascript
const { Worker } = require('worker_threads'); new Worker('./worker.js'); // окремий потік для важких обчислень

Підсумок

ХарактеристикаNode.js
Потік для JS-кодуОдин (event loop)
Потоки під капотомТак (у libuv)
Виконання JS паралельноНі
АсинхронністьЧерез event loop
Багатопоточність доступна вручнуТак (через worker_threads, cluster)

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

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

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