worker_threads у Node.js
Що таке worker_threads
worker_threads - це вбудований модуль Node.js,
який дозволяє запускати JavaScript-код в окремих потоках (workers).
Простіше кажучи: це спосіб виконувати кілька JS-задач паралельно, використовуючи різні CPU-ядра, без блокування основного потоку (event loop).
Навіщо він потрібен
Node.js чудово справляється з асинхронними I/O-операціями (мережевими, файловими, БД). Але якщо код виконує CPU-інтенсивну задачу (наприклад, шифрування, парсинг, обробку зображень) - вона блокує event loop, і сервер перестає відповідати на інші запити.
Рішення: виносити важкі обчислення в окремі потоки за допомогою worker_threads.
1. Як підключити
Модуль вбудований у Node.js (починаючи з версії 10.5, стабільний із 12):
const { Worker, isMainThread, parentPort } = require('worker_threads');2. Найпростіший приклад
main.js
const { Worker } = require('worker_threads');
console.log('Головний потік:', process.pid);
const worker = new Worker('./worker.js');
worker.on('message', msg => console.log('Результат від воркера:', msg));
worker.on('exit', () => console.log('Воркер завершив роботу'));worker.js
console.log('Воркер запущено:', process.pid);
let sum = 0;
for (let i = 0; i < 1e9; i++) sum += i;
postMessage(sum); // Надсилаємо результат у головний потікЩо відбувається:
main.jsзапускає воркер в окремому потоці.- Воркер виконує важку задачу (цикл на мільярд ітерацій).
- Основний потік не блокується.
- Коли задача завершена - результат повертається назад через повідомлення.
3. Як працює обмін даними
Між потоками немає спільного контексту (як у багатопроцесних мовах). Вони обмінюються повідомленнями (message passing).
| Механізм | Приклад |
|---|---|
parentPort.postMessage(data) | Воркер надсилає повідомлення в основний потік |
worker.on('message', handler) | Основний потік слухає відповідь |
workerData | Можна передати дані під час створення воркера |
Приклад передавання аргументів:
const { Worker } = require('worker_threads');
const worker = new Worker('./worker.js', {
workerData: { iterations: 1e7 }
});worker.js
const { workerData, parentPort } = require('worker_threads');
let sum = 0;
for (let i = 0; i < workerData.iterations; i++) sum += i;
parentPort.postMessage(sum);4. Асинхронна версія (через проміс)
Для зручності можна обгорнути Worker у проміс:
const { Worker } = require('worker_threads');
function runWorker(path, data) {
return new Promise((resolve, reject) => {
const worker = new Worker(path, { workerData: data });
worker.on('message', resolve);
worker.on('error', reject);
worker.on('exit', code => {
if (code !== 0) reject(new Error(`Worker stopped with code ${code}`));
});
});
}
(async () => {
const result = await runWorker('./task.js', { n: 1e8 });
console.log('Результат:', result);
})();5. Коли використовувати worker_threads
| Підходить для | Не підходить для |
|---|---|
| CPU-інтенсивних задач | Звичайного I/O (HTTP, файли, БД) |
| Парсингу й обробки великих даних | Простих API-запитів |
| Шифрування, стиснення | Асинхронного введення-виведення |
| Рендерингу, генерації PDF/зображень | Простої логіки |
6. Відмінність від інших способів
| Підхід | Модуль | Процес чи потік | Обмін даними |
|---|---|---|---|
| worker_threads | worker_threads | Потік (спільна пам'ять) | Швидкий, через повідомлення |
| cluster | cluster | Окремі процеси Node.js | IPC (повільніше) |
| child_process | child_process | Окремий процес | Через stdout/stdin |
worker_threads працює в тому самому процесі, але з окремим потоком JS,
тому вони легші й швидші, ніж процеси cluster чи child_process.
7. Додаткові можливості
- Можна використовувати SharedArrayBuffer для спільної пам'яті.
- Можна передавати Transferable об'єкти (ArrayBuffer, MessagePort).
- Підтримуються Atomics для синхронізації даних.
- Підтримка TypeScript і ES Modules повністю працює.
8. Важливо пам'ятати
- Потоки-worker не діляться спільним
globalчиrequire- у кожного свій контекст. - Занадто багато воркерів → великі накладні витрати на пам'ять і комунікацію.
- Воркер не робить Node.js «багатопоточним» у звичному сенсі - ти сам вирішуєш, коли створювати потоки.
Підсумок
| Критерій | Опис |
|---|---|
| Призначення | Паралельне виконання JS-коду в окремих потоках |
| Модуль | worker_threads |
| Обмін даними | Через postMessage / parentPort |
| Продуктивність | Легше й швидше, ніж процеси |
| Підходить для | CPU-інтенсивних задач (математика, парсинг, шифрування) |
| З'явився | У Node.js 10.5 (стабільний із 12+) |
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.