Что такое Worker Threads в Node.js и для чего они нужны?

«Что такое Worker Threads в Node.js и для чего они нужны?» — вопрос из категории Node.js Core, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Worker Threads — это механизм в Node.js для выполнения JavaScript-кода в параллельных потоках. Они предназначены для выгрузки CPU-интенсивных (блокирующих) задач из основного потока Event Loop, чтобы не блокировать обработку входящих запросов и асинхронных операций I/O.

Ключевые отличия от кластеризации (cluster):

  • Кластер (cluster): Создаёт несколько независимых процессов (инстансов Node.js), каждый со своей памятью. Подходит для масштабирования сетевых приложений, так как процессы не разделяют память и общаются через IPC.
  • Worker Threads: Создаёт несколько потоков в рамках одного процесса. Потоки могут (опционально) разделять память через SharedArrayBuffer, что делает их более подходящими для параллельных вычислений над общими данными.

Основные концепции и API:

  • Worker: Класс для создания нового потока.
  • parentPort: Канал для обмена сообщениями между главным потоком и воркером.
  • workerData: Позволяет передать начальные данные в воркер при его создании.
  • MessageChannel: Создаёт канал для прямого общения между воркерами.

Пример: вычисление числа Фибоначчи в отдельном потоке

main.js (главный поток):

const { Worker } = require('worker_threads');
const path = require('path');

function runFibonacciService(n) {
    return new Promise((resolve, reject) => {
        // Создаём воркер и передаём ему данные
        const worker = new Worker(path.join(__dirname, 'fibonacci-worker.js'), {
            workerData: n
        });

        worker.on('message', resolve); // Получаем результат
        worker.on('error', reject);    // Обрабатываем ошибки в воркере
        worker.on('exit', (code) => {
            if (code !== 0) {
                reject(new Error(`Worker stopped with exit code ${code}`));
            }
        });
    });
}

// Использование
(async () => {
    console.log('Main thread: starting heavy calculation...');
    const result = await runFibonacciService(40); // Блокирующая операция
    console.log(`Main thread: result = ${result}`);
    // Event Loop в основном потоке не был заблокирован!
})();

fibonacci-worker.js (код воркера):

const { parentPort, workerData } = require('worker_threads');

// Рекурсивная функция, тяжелая для CPU
function fibonacci(n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

// Выполняем вычисление и отправляем результат обратно в главный поток
const result = fibonacci(workerData);
parentPort.postMessage(result);

Когда использовать Worker Threads:

  • Обработка изображений/видео.
  • Синтаксический анализ или сжатие больших объёмов данных.
  • Сложные математические вычисления, машинное обучение.
  • Любые синхронные задачи, которые занимают более 10-30 мс и блокируют Event Loop.

Важно: Worker Threads не предназначены для увеличения производительности I/O-операций (работа с файлами, сетью). Для этого эффективнее использовать асинхронные API самого Node.js.