Spec-Zone.ru › Node.js 20 LTS

Рабочие потоки

Стабильность: 2 - Стабильный

Исходный код: lib/worker_threads.js

Модуль node:worker_threads позволяет использовать потоки, выполняющие JavaScript параллельно. Для доступа к нему:

const worker = require('node:worker_threads'); copy

Рабочие потоки (потоки) полезны для выполнения ресурсоёмких операций JavaScript, интенсивно использующих процессор. Они мало помогают при выполнении операций, интенсивно использующих ввод-вывод. Встроенные асинхронные операции ввода-вывода Node.js эффективнее рабочих потоков.

В отличие от child_process или cluster, worker_threads могут совместно использовать память. Это достигается передачей экземпляров ArrayBuffer или совместным использованием экземпляров SharedArrayBuffer.

const {
  Worker, isMainThread, parentPort, workerData,
} = require('node:worker_threads');

if (isMainThread) {
  module.exports = function parseJSAsync(script) {
    return new Promise((resolve, reject) => {
      const worker = new Worker(__filename, {
        workerData: script,
      });
      worker.on('message', resolve);
      worker.on('error', reject);
      worker.on('exit', (code) => {
        if (code !== 0)
          reject(new Error(`Worker stopped with exit code ${code}`));
      });
    });
  };
} else {
  const { parse } = require('some-js-parsing-library');
  const script = workerData;
  parentPort.postMessage(parse(script));
} copy

В приведённом выше примере для каждого вызова parseJSAsync() создаётся рабочий поток. На практике для выполнения таких задач используйте пул рабочих потоков. В противном случае накладные расходы на создание рабочих потоков, вероятно, превысят их пользу.

При реализации пула рабочих потоков используйте API AsyncResource, чтобы сообщить средствам диагностики (например, предоставить асинхронные трассировки стека) о связи между задачами и результатами их выполнения. Пример реализации см. в разделе «Использование AsyncResource для пула потоков Worker» в документации async_hooks.

Рабочие потоки по умолчанию наследуют параметры, не относящиеся к конкретному процессу. См. Worker constructor options, чтобы узнать, как настроить параметры рабочего потока, в частности параметры argv и execArgv.

worker.getEnvironmentData(key)

История
Версия Изменения
v17.5.0, v16.15.0

Больше не является экспериментальным.

v15.12.0, v14.18.0

Добавлено в: v15.12.0, v14.18.0

  • key <any> Любое произвольное клонируемое значение JavaScript, которое можно использовать в качестве ключа <Map>.
  • Возвращает: <any>

В рабочем потоке worker.getEnvironmentData() возвращает клон данных, переданных в worker.setEnvironmentData() потока, который его создал. Каждый новый Worker автоматически получает собственную копию данных среды.

const {
  Worker,
  isMainThread,
  setEnvironmentData,
  getEnvironmentData,
} = require('node:worker_threads');

if (isMainThread) {
  setEnvironmentData('Hello', 'World!');
  const worker = new Worker(__filename);
} else {
  console.log(getEnvironmentData('Hello'));  // Prints 'World!'.
} copy

worker.isMainThread

Добавлено в: v10.5.0
  • <boolean>

Равно true, если этот код выполняется не внутри потока Worker.

const { Worker, isMainThread } = require('node:worker_threads');

if (isMainThread) {
  // This re-loads the current file inside a Worker instance.
  new Worker(__filename);
} else {
  console.log('Inside Worker!');
  console.log(isMainThread);  // Prints 'false'.
} copy

worker.markAsUntransferable(object)

Добавлено в: v14.5.0, v12.19.0

Помечает объект как непередаваемый. Если object указан в списке передачи вызова port.postMessage(), он игнорируется.

Это особенно полезно для объектов, которые можно клонировать, а не передавать, и которые используются другими объектами на стороне отправителя. Например, Node.js таким образом помечает ArrayBuffer, используемые для пула Buffer.

Эту операцию нельзя отменить.

const { MessageChannel, markAsUntransferable } = require('node:worker_threads');

const pooledBuffer = new ArrayBuffer(8);
const typedArray1 = new Uint8Array(pooledBuffer);
const typedArray2 = new Float64Array(pooledBuffer);

markAsUntransferable(pooledBuffer);

const { port1 } = new MessageChannel();
port1.postMessage(typedArray1, [ typedArray1.buffer ]);

// The following line prints the contents of typedArray1 -- it still owns
// its memory and has been cloned, not transferred. Without
// `markAsUntransferable()`, this would print an empty Uint8Array.
// typedArray2 is intact as well.
console.log(typedArray1);
console.log(typedArray2); copy

В браузерах нет эквивалента этому API.

worker.moveMessagePortToContext(port, contextifiedSandbox)

Добавлено в: v11.13.0
  • port <MessagePort> Порт сообщений для передачи.

  • contextifiedSandbox <Object> Контекстуализированный объект, возвращённый методом vm.createContext().

  • Возвращает: <MessagePort>

Передаёт MessagePort в другой контекст vm. Исходный объект port становится непригодным для использования, и его заменяет возвращённый экземпляр MessagePort.

Возвращённый MessagePort является объектом в целевом контексте и наследуется от глобального класса Object этого контекста. Объекты, переданные обработчику port.onmessage(), также создаются в целевом контексте и наследуются от глобального класса Object этого контекста.

Однако созданный MessagePort больше не наследуется от EventTarget, и для получения событий с его помощью можно использовать только port.onmessage().

worker.parentPort

Добавлено в: v10.5.0
  • <null> | <MessagePort>

Если этот поток является потоком Worker, это объект MessagePort, обеспечивающий связь с родительским потоком. Сообщения, отправленные с помощью parentPort.postMessage(), доступны в родительском потоке через worker.on('message'), а сообщения, отправленные из родительского потока с помощью worker.postMessage(), доступны в этом потоке через parentPort.on('message').

const { Worker, isMainThread, parentPort } = require('node:worker_threads');

if (isMainThread) {
  const worker = new Worker(__filename);
  worker.once('message', (message) => {
    console.log(message);  // Prints 'Hello, world!'.
  });
  worker.postMessage('Hello, world!');
} else {
  // When a message from the parent thread is received, send it back:
  parentPort.once('message', (message) => {
    parentPort.postMessage(message);
  });
} copy

worker.postMessageToThread(threadId, value[, transferList][, timeout])

Добавлено в: v20.19.0
Стабильность: 1.1 — Активная разработка
  • destination <number> Идентификатор целевого потока. Если идентификатор потока недопустим, будет выброшена ошибка ERR_WORKER_MESSAGING_FAILED. Если идентификатор целевого потока совпадает с идентификатором текущего потока, будет выброшена ошибка ERR_WORKER_MESSAGING_SAME_THREAD.
  • value <any> Отправляемое значение.
  • transferList <Object[]> Если в value передан один или несколько объектов типа MessagePort, для этих элементов требуется transferList, иначе будет выброшена ошибка ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST. Дополнительные сведения см. в разделе port.postMessage().
  • timeout <number> Время ожидания доставки сообщения в миллисекундах. По умолчанию равно undefined, что означает бесконечное ожидание. Если время ожидания операции истечёт, будет выброшена ошибка ERR_WORKER_MESSAGING_TIMEOUT.
  • Возвращает: <Promise> Промис, который выполнится, если целевой поток успешно обработает сообщение.

Отправляет значение другому рабочему потоку, заданному его идентификатором потока.

Если в целевом потоке нет обработчика события workerMessage, операция выбросит ошибку ERR_WORKER_MESSAGING_FAILED.

Если целевой поток выбросил ошибку при обработке события workerMessage, операция выбросит ошибку ERR_WORKER_MESSAGING_ERRORED.

Этот метод следует использовать, если целевой поток не является непосредственным родительским или дочерним потоком текущего потока. Если два потока связаны отношением родителя и потомка, используйте require('node:worker_threads').parentPort.postMessage() и worker.postMessage(), чтобы обеспечить обмен данными между потоками.

В приведённом ниже примере показано использование postMessageToThread: создаются 10 вложенных потоков, и последний пытается связаться с главным потоком.

Модули JavaScript
import { fileURLToPath } from 'node:url';
import { once } from 'node:events';
import process from 'node:process';
import {
  isMainThread,
  postMessageToThread,
  threadId,
  workerData,
  Worker,
} from 'node:worker_threads';

const channel = new BroadcastChannel('sync');
const level = workerData?.level ?? 0;

if (level < 10) {
  const worker = new Worker(fileURLToPath(import.meta.url), {
    workerData: { level: level + 1 },
  });
}

if (level === 0) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value);
    postMessageToThread(source, { message: 'pong' });
  });
} else if (level === 10) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value);
    channel.postMessage('done');
    channel.close();
  });

  await postMessageToThread(0, { message: 'ping' });
}

channel.onmessage = channel.close;
CommonJS
const { once } = require('node:events');
const {
  isMainThread,
  postMessageToThread,
  threadId,
  workerData,
  Worker,
} = require('node:worker_threads');

const channel = new BroadcastChannel('sync');
const level = workerData?.level ?? 0;

if (level < 10) {
  const worker = new Worker(__filename, {
    workerData: { level: level + 1 },
  });
}

if (level === 0) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value);
    postMessageToThread(source, { message: 'pong' });
  });
} else if (level === 10) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value);
    channel.postMessage('done');
    channel.close();
  });

  postMessageToThread(0, { message: 'ping' });
}

channel.onmessage = channel.close;

worker.receiveMessageOnPort(port)

История
Версия Изменения
v15.12.0

Теперь аргумент порта также может ссылаться на BroadcastChannel.

v12.3.0

Добавлено в: v12.3.0

  • port <MessagePort> | <BroadcastChannel>

  • Возвращает: <Object> | <undefined>

Получает одно сообщение из заданного MessagePort. Если сообщение недоступно, возвращается undefined, в противном случае возвращается объект с единственным свойством message, содержащим полезную нагрузку сообщения, соответствующего самому старому сообщению в очереди MessagePort.

const { MessageChannel, receiveMessageOnPort } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();
port1.postMessage({ hello: 'world' });

console.log(receiveMessageOnPort(port2));
// Prints: { message: { hello: 'world' } }
console.log(receiveMessageOnPort(port2));
// Prints: undefined copy

При использовании этой функции событие 'message' не генерируется, а обработчик onmessage не вызывается.

worker.resourceLimits

Добавлено в: v13.2.0, v12.16.0
  • <Object>
    • maxYoungGenerationSizeMb <number>
    • maxOldGenerationSizeMb <number>
    • codeRangeSizeMb <number>
    • stackSizeMb <number>

Предоставляет набор ограничений на ресурсы движка JS в этом рабочем потоке. Если конструктору Worker был передан параметр resourceLimits, здесь будут указаны соответствующие значения.

При использовании в главном потоке возвращается пустой объект.

worker.SHARE_ENV

Добавлено в: v11.14.0
  • <symbol>

Специальное значение, которое можно передать как параметр env конструктора Worker, чтобы указать, что текущий поток и рабочий поток должны совместно использовать один и тот же набор переменных среды с правами на чтение и запись.

const { Worker, SHARE_ENV } = require('node:worker_threads');
new Worker('process.env.SET_IN_WORKER = "foo"', { eval: true, env: SHARE_ENV })
  .on('exit', () => {
    console.log(process.env.SET_IN_WORKER);  // Prints 'foo'.
  }); copy

worker.setEnvironmentData(key[, value])

История
Версия Изменения
v17.5.0, v16.15.0

Больше не является экспериментальным.

v15.12.0, v14.18.0

Добавлено в: v15.12.0, v14.18.0

  • key <any> Любое произвольное клонируемое значение JavaScript, которое можно использовать в качестве ключа <Map>.
  • value <any> Любое произвольное клонируемое значение JavaScript, которое будет клонировано и автоматически передано всем новым экземплярам Worker. Если в качестве undefined передано value, любое ранее заданное значение для key будет удалено.

API worker.setEnvironmentData() задаёт содержимое worker.getEnvironmentData() в текущем потоке и во всех новых экземплярах Worker, созданных из текущего контекста.

worker.threadId

Добавлено в: v10.5.0
  • <integer>

Целочисленный идентификатор текущего потока. В соответствующем объекте рабочего потока (если он существует) это значение доступно как worker.threadId. Это значение уникально для каждого экземпляра Worker в рамках одного процесса.

worker.workerData

Добавлено в: v10.5.0

Произвольное значение JavaScript, содержащее копию данных, переданных конструктору Worker этого потока.

Данные клонируются так же, как при использовании postMessage(), в соответствии с алгоритмом структурированного клонирования HTML.

const { Worker, isMainThread, workerData } = require('node:worker_threads');

if (isMainThread) {
  const worker = new Worker(__filename, { workerData: 'Hello, world!' });
} else {
  console.log(workerData);  // Prints 'Hello, world!'.
} copy

Класс: BroadcastChannel extends EventTarget

История
Версия Изменения
v18.0.0

Больше не является экспериментальным.

v15.4.0

Добавлено в: v15.4.0

Экземпляры BroadcastChannel обеспечивают асинхронную связь «один ко многим» со всеми другими экземплярами BroadcastChannel, связанными с тем же именем канала.

'use strict';

const {
  isMainThread,
  BroadcastChannel,
  Worker,
} = require('node:worker_threads');

const bc = new BroadcastChannel('hello');

if (isMainThread) {
  let c = 0;
  bc.onmessage = (event) => {
    console.log(event.data);
    if (++c === 10) bc.close();
  };
  for (let n = 0; n < 10; n++)
    new Worker(__filename);
} else {
  bc.postMessage('hello from every worker');
  bc.close();
} copy

new BroadcastChannel(name)

Добавлено в: v15.4.0
  • name <any> Имя канала для подключения. Допускается любое значение JavaScript, которое можно преобразовать в строку с помощью `${name}`.

broadcastChannel.close()

Добавлено в: v15.4.0

Закрывает соединение BroadcastChannel.

broadcastChannel.onmessage

Добавлено в: v15.4.0
  • Тип: <Function> Вызывается с одним аргументом MessageEvent при получении сообщения.

broadcastChannel.onmessageerror

Добавлено в: v15.4.0
  • Тип: <Function> Вызывается, если полученное сообщение не удаётся десериализовать.

broadcastChannel.postMessage(message)

Добавлено в: v15.4.0
  • message <any> Любое клонируемое значение JavaScript.

broadcastChannel.ref()

Добавлено в: v15.4.0

Противоположность unref(). Вызов ref() для BroadcastChannel, ранее подвергнутого операции unref(), не позволяет программе завершиться, если это единственный оставшийся активный дескриптор (поведение по умолчанию). Если для порта была вызвана операция ref(), повторный вызов ref() не даст эффекта.

broadcastChannel.unref()

Добавлено в: v15.4.0

Вызов unref() для BroadcastChannel позволяет потоку завершиться, если это единственный активный дескриптор в системе событий. Если для BroadcastChannel уже была вызвана операция unref(), повторный вызов unref() не даст эффекта.

Класс: MessageChannel

Добавлено в: v10.5.0

Экземпляры класса worker.MessageChannel представляют собой асинхронный двусторонний канал связи. У MessageChannel нет собственных методов. new MessageChannel() возвращает объект со свойствами port1 и port2, которые ссылаются на связанные экземпляры MessagePort.

const { MessageChannel } = require('node:worker_threads');

const { port1, port2 } = new MessageChannel();
port1.on('message', (message) => console.log('received', message));
port2.postMessage({ foo: 'bar' });
// Prints: received { foo: 'bar' } from the `port1.on('message')` listener copy

Класс: MessagePort

История
Версия Изменения
v14.7.0

Теперь этот класс наследуется от EventTarget, а не от EventEmitter.

v10.5.0

Добавлено в: v10.5.0

  • Наследуется от: <EventTarget>

Экземпляры класса worker.MessagePort представляют один конец асинхронного двунаправленного канала связи. Его можно использовать для передачи структурированных данных, областей памяти и других MessagePort между разными Worker.

Эта реализация соответствует MessagePort браузера.

Событие: 'close'

Добавлено в: v10.5.0

Событие 'close' возникает при отключении любой из сторон канала.

const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();

// Prints:
//   foobar
//   closed!
port2.on('message', (message) => console.log(message));
port2.on('close', () => console.log('closed!'));

port1.postMessage('foobar');
port1.close(); copy

Событие: 'message'

Добавлено в: v10.5.0
  • value <any> Переданное значение

Событие 'message' возникает при получении любого входящего сообщения и содержит клонированные входные данные, переданные в port.postMessage().

Обработчики этого события получают копию параметра value, переданного в postMessage(), без дополнительных аргументов.

Событие: 'messageerror'

Добавлено в: v14.5.0, v12.19.0
  • error <Error> Объект Error

Событие 'messageerror' возникает при сбое десериализации сообщения.

В настоящее время это событие возникает при ошибке во время создания отправленного объекта JS на стороне получателя. Такие ситуации редки, но могут возникнуть, например, когда определенные объекты API Node.js получены в vm.Context (где API Node.js в настоящее время недоступны).

port.close()

Добавлено в: v10.5.0

Запрещает дальнейшую отправку сообщений с любой стороны соединения. Этот метод можно вызвать, если дальнейшее взаимодействие через этот MessagePort не предполагается.

Событие 'close' возникает для обоих экземпляров MessagePort, участвующих в канале.

port.postMessage(value[, transferList])

История
Версия Изменения
v15.6.0

X509Certificate добавлен в список клонируемых типов.

v15.0.0

CryptoKey добавлен в список клонируемых типов.

v15.14.0, v14.18.0

'BlockList' добавлен в список клонируемых типов.

v15.9.0, v14.18.0

Типы 'Histogram' добавлены в список клонируемых типов.

v14.5.0, v12.19.0

KeyObject добавлен в список клонируемых типов.

v14.5.0, v12.19.0

FileHandle добавлен в список передаваемых типов.

v10.5.0

Добавлено в: v10.5.0

  • value <any>
  • transferList <Object[]>

Отправляет значение JavaScript на принимающую сторону этого канала. value передается способом, совместимым с алгоритмом структурированного клонирования HTML.

В частности, существенные отличия от JSON заключаются в следующем:

  • value может содержать циклические ссылки.
  • value может содержать экземпляры встроенных типов JS, таких как RegExp, BigInt, Map, Set и т. д.
  • value может содержать типизированные массивы, использующие как ArrayBuffer, так и SharedArrayBuffer.
  • value может содержать экземпляры WebAssembly.Module.
  • value не может содержать объекты с нативной реализацией (на C++), за исключением:
    • <CryptoKey>,
    • <FileHandle>,
    • <Histogram>,
    • <KeyObject>,
    • <MessagePort>,
    • <net.BlockList>,
    • <net.SocketAddress>,
    • <X509Certificate>.
const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();

port1.on('message', (message) => console.log(message));

const circularData = {};
circularData.foo = circularData;
// Prints: { foo: [Circular] }
port2.postMessage(circularData); copy

transferList может быть списком объектов ArrayBuffer, MessagePort и FileHandle. После передачи они становятся недоступны на отправляющей стороне канала, даже если не входят в value. В отличие от дочерних процессов, передача дескрипторов, например сетевых сокетов, в настоящее время не поддерживается.

Если value содержит экземпляры SharedArrayBuffer, к ним имеют доступ оба потока. Их нельзя указывать в transferList.

value может по-прежнему содержать экземпляры ArrayBuffer, отсутствующие в transferList; в этом случае базовая память копируется, а не перемещается.

const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();

port1.on('message', (message) => console.log(message));

const uint8Array = new Uint8Array([ 1, 2, 3, 4 ]);
// This posts a copy of `uint8Array`:
port2.postMessage(uint8Array);
// This does not copy data, but renders `uint8Array` unusable:
port2.postMessage(uint8Array, [ uint8Array.buffer ]);

// The memory for the `sharedUint8Array` is accessible from both the
// original and the copy received by `.on('message')`:
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4));
port2.postMessage(sharedUint8Array);

// This transfers a freshly created message port to the receiver.
// This can be used, for example, to create communication channels between
// multiple `Worker` threads that are children of the same parent thread.
const otherChannel = new MessageChannel();
port2.postMessage({ port: otherChannel.port1 }, [ otherChannel.port1 ]); copy

Объект сообщения клонируется немедленно, поэтому после отправки его можно изменять без побочных эффектов.

Дополнительные сведения о механизмах сериализации и десериализации, лежащих в основе этого API, см. в разделе API сериализации модуля node:v8.

Особенности передачи TypedArray и Buffer

Все экземпляры TypedArray и Buffer являются представлениями базового ArrayBuffer. Иными словами, именно ArrayBuffer хранит исходные данные, а объекты TypedArray и Buffer предоставляют способ просмотра этих данных и управления ими. Часто для одного экземпляра ArrayBuffer создается несколько представлений. При использовании списка передачи для передачи ArrayBuffer необходимо проявлять особую осторожность, поскольку в результате все экземпляры TypedArray и Buffer, использующие тот же ArrayBuffer, станут недоступны.

const ab = new ArrayBuffer(10);

const u1 = new Uint8Array(ab);
const u2 = new Uint16Array(ab);

console.log(u2.length);  // prints 5

port.postMessage(u1, [u1.buffer]);

console.log(u2.length);  // prints 0 copy

В частности, для экземпляров Buffer возможность передачи или клонирования базового ArrayBuffer полностью зависит от способа создания экземпляров, который часто невозможно надежно определить.

Для ArrayBuffer можно вызвать markAsUntransferable(), чтобы указать, что объект всегда следует клонировать, а не передавать.

В зависимости от способа создания экземпляра Buffer он может владеть базовым ArrayBuffer или не владеть им. ArrayBuffer нельзя передавать, если неизвестно, что экземпляр Buffer владеет им. В частности, Buffer, созданные из внутреннего пула Buffer (например, с помощью Buffer.from() или Buffer.allocUnsafe()), передавать нельзя, поэтому они всегда клонируются, что приводит к отправке копии всего пула Buffer. Такое поведение может привести к непреднамеренно повышенному расходу памяти и потенциальным проблемам безопасности.

Подробнее о пуле Buffer см. в разделе Buffer.allocUnsafe().

Объекты ArrayBuffer для экземпляров Buffer, созданных с помощью Buffer.alloc() или Buffer.allocUnsafeSlow(), всегда можно передавать, однако при этом все остальные существующие представления этих ArrayBuffer станут недоступны.

Особенности клонирования объектов с прототипами, классами и аксессорами

Поскольку при клонировании объектов используется алгоритм структурированного клонирования HTML, неперечисляемые свойства, аксессоры свойств и прототипы объектов не сохраняются. В частности, объекты Buffer на принимающей стороне будут представлены как обычные Uint8Array, а экземпляры классов JavaScript будут клонированы как обычные объекты JavaScript.

const b = Symbol('b');

class Foo {
  #a = 1;
  constructor() {
    this[b] = 2;
    this.c = 3;
  }

  get d() { return 4; }
}

const { port1, port2 } = new MessageChannel();

port1.onmessage = ({ data }) => console.log(data);

port2.postMessage(new Foo());

// Prints: { c: 3 } copy

Это ограничение распространяется на многие встроенные объекты, например на глобальный объект URL:

const { port1, port2 } = new MessageChannel();

port1.onmessage = ({ data }) => console.log(data);

port2.postMessage(new URL('https://example.org'));

// Prints: { } copy

port.hasRef()

Добавлено в: v18.1.0, v16.17.0
Стабильность: 1 — Экспериментальный
  • Возвращает: <boolean>

Если значение равно true, объект MessagePort будет поддерживать активность цикла событий Node.js.

port.ref()

Добавлено в: v10.5.0

Противоположность unref(). Вызов ref() для порта, ранее вызвавшего unref(), не позволит программе завершиться, если это единственный оставшийся активный дескриптор (поведение по умолчанию). Если порт вызвал ref(), повторный вызов ref() не повлияет на результат.

При добавлении или удалении обработчиков с помощью .on('message') порт автоматически вызывает ref() или unref() в зависимости от наличия обработчиков события.

port.start()

Добавлено в: v10.5.0

Начинает получать сообщения через этот MessagePort. При использовании этого порта в качестве генератора событий метод вызывается автоматически после добавления обработчиков 'message'.

Этот метод существует для соответствия API Web MessagePort. В Node.js он полезен только для игнорирования сообщений, когда обработчик событий отсутствует. Также Node.js иначе обрабатывает .onmessage. Его установка автоматически вызывает .start(), но при удалении сообщения остаются в очереди, пока не будет задан новый обработчик или порт не будет удален.

port.unref()

Добавлено в: v10.5.0

Вызов unref() для порта позволяет потоку завершиться, если это единственный активный дескриптор в системе событий. Если порт уже вызвал unref(), повторный вызов unref() не повлияет на результат.

При добавлении или удалении обработчиков с помощью .on('message') порт автоматически вызывает ref() или unref() в зависимости от наличия обработчиков события.

Класс: Worker

Добавлено в: v10.5.0
  • Расширяет: <EventEmitter>

Класс Worker представляет собой независимый поток выполнения JavaScript. В нём доступны большинство API Node.js.

Примечательные отличия среды Worker:

  • Потоки process.stdin, process.stdout и process.stderr могут перенаправляться родительским потоком.
  • Свойству require('node:worker_threads').isMainThread присваивается значение false.
  • Доступен порт сообщений require('node:worker_threads').parentPort.
  • process.exit() останавливает не всю программу, а только отдельный поток; process.abort() недоступен.
  • Метод process.chdir() и методы process, изменяющие идентификаторы группы или пользователя, недоступны.
  • process.env — это копия переменных среды родительского потока, если не указано иное. Изменения в одной копии не видны в других потоках и недоступны нативным дополнениям (если только worker.SHARE_ENV не передан как параметр env конструктору Worker). В Windows, в отличие от главного потока, в копии переменных среды учитывается регистр.
  • process.title изменить нельзя.
  • Сигналы не доставляются через process.on('...').
  • Выполнение может остановиться в любой момент в результате вызова worker.terminate().
  • Канал IPC родительских процессов недоступен.
  • Модуль trace_events не поддерживается.
  • Нативные дополнения можно загружать из нескольких потоков, только если они соответствуют определённым условиям.

Можно создавать экземпляры Worker внутри других Worker.

Как и в случае с веб-воркерами и модулем node:cluster, двустороннюю связь можно реализовать с помощью обмена сообщениями между потоками. Внутри Worker есть встроенная пара объектов MessagePort, уже связанных друг с другом при создании Worker. Хотя объект MessagePort родительской стороны напрямую не предоставляется, его функциональность доступна через worker.postMessage() и событие worker.on('message') объекта Worker родительского потока.

Чтобы создавать пользовательские каналы обмена сообщениями (что рекомендуется вместо использования глобального канала по умолчанию, поскольку это способствует разделению ответственности), можно создать объект MessageChannel в любом из потоков и передать один из объектов MessagePort этого MessageChannel в другой поток по уже существующему каналу, например глобальному.

Дополнительные сведения о передаче сообщений и о том, какие значения JavaScript можно успешно передавать между потоками, см. в разделе port.postMessage().

const assert = require('node:assert');
const {
  Worker, MessageChannel, MessagePort, isMainThread, parentPort,
} = require('node:worker_threads');
if (isMainThread) {
  const worker = new Worker(__filename);
  const subChannel = new MessageChannel();
  worker.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]);
  subChannel.port2.on('message', (value) => {
    console.log('received:', value);
  });
} else {
  parentPort.once('message', (value) => {
    assert(value.hereIsYourPort instanceof MessagePort);
    value.hereIsYourPort.postMessage('the worker is sending this');
    value.hereIsYourPort.close();
  });
} copy

new Worker(filename[, options])

История
Версия Изменения
v19.8.0

Добавлена поддержка параметра name, который позволяет задать имя заголовка рабочего потока для отладки.

v14.9.0

Параметр filename может быть объектом WHATWG URL, использующим протокол data:.

v14.9.0

Для параметра trackUnmanagedFds по умолчанию было установлено значение true.

v14.6.0, v12.19.0

Добавлен параметр trackUnmanagedFds.

v13.13.0, v12.17.0

Добавлен параметр transferList.

v13.12.0, v12.17.0

Параметр filename может быть объектом WHATWG URL, использующим протокол file:.

v13.4.0, v12.16.0

Добавлен параметр argv.

v13.2.0, v12.16.0

Добавлен параметр resourceLimits.

v10.5.0

Добавлено в: v10.5.0

  • filename <string> | <URL> Путь к основному скрипту или модулю Worker. Должен быть абсолютным или относительным путём (то есть относительно текущего рабочего каталога), начинающимся с ./ или ../, либо объектом WHATWG URL, использующим протокол file: или data:. При использовании URL-адреса data: данные интерпретируются на основе MIME-типа с помощью загрузчика модулей ECMAScript. Если options.eval имеет значение true, это строка с кодом JavaScript, а не путь.
  • options <Object>
    • argv <any[]> Список аргументов, которые будут преобразованы в строки и добавлены к process.argv в рабочем потоке. Это в основном аналогично workerData, но значения доступны в глобальном объекте process.argv так, как если бы они были переданы скрипту в качестве параметров CLI.
    • env <Object> Если задано, определяет начальное значение process.env в потоке Worker. В качестве специального значения можно использовать worker.SHARE_ENV, чтобы указать, что родительский и дочерний потоки должны совместно использовать переменные среды; в этом случае изменения объекта process.env одного потока также влияют на другой поток. По умолчанию: process.env.
    • eval <boolean> Если имеет значение true и первый аргумент является string, первый аргумент конструктора интерпретируется как скрипт, который выполняется после перехода рабочего потока в состояние готовности.
    • execArgv <string[]> Список параметров CLI Node, передаваемых рабочему потоку. Параметры V8 (например, --max-old-space-size) и параметры, влияющие на процесс (например, --title), не поддерживаются. Если задано, это значение предоставляется в рабочем потоке как process.execArgv. По умолчанию параметры наследуются от родительского потока.
    • stdin <boolean> Если задано значение true, worker.stdin предоставляет доступный для записи поток, содержимое которого отображается как process.stdin внутри Worker. По умолчанию данные не предоставляются.
    • stdout <boolean> Если задано значение true, worker.stdout не передаётся автоматически в process.stdout родительского потока.
    • stderr <boolean> Если задано значение true, worker.stderr не передаётся автоматически в process.stderr родительского потока.
    • workerData <any> Любое значение JavaScript, которое клонируется и становится доступным как require('node:worker_threads').workerData. Клонирование выполняется согласно алгоритму структурированного клонирования HTML; если объект невозможно клонировать (например, потому что он содержит function), возникает ошибка.
    • trackUnmanagedFds <boolean> Если задано значение true, Worker отслеживает необработанные файловые дескрипторы, управляемые с помощью fs.open() и fs.close(), и закрывает их при завершении работы Worker, как и другие ресурсы, например сетевые сокеты или файловые дескрипторы, управляемые через API FileHandle. Этот параметр автоматически наследуется всеми вложенными объектами Worker. По умолчанию: true.
    • transferList <Object[]> Если в workerData передан один или несколько объектов типа MessagePort, для этих элементов требуется transferList, иначе возникает ошибка ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST. Дополнительные сведения см. в разделе port.postMessage().
    • resourceLimits <Object> Необязательный набор ограничений ресурсов для нового экземпляра движка JS. Превышение этих ограничений приводит к завершению экземпляра Worker. Ограничения влияют только на движок JS и не распространяются на внешние данные, включая ArrayBuffer. Даже при заданных ограничениях процесс всё ещё может аварийно завершиться при глобальной нехватке памяти.
      • maxOldGenerationSizeMb <number> Максимальный размер основной кучи в МБ. Если задан аргумент командной строки --max-old-space-size, он переопределяет это значение.
      • maxYoungGenerationSizeMb <number> Максимальный размер области кучи для недавно созданных объектов. Если задан аргумент командной строки --max-semi-space-size, он переопределяет это значение.
      • codeRangeSizeMb <number> Размер предварительно выделяемой области памяти, используемой для сгенерированного кода.
      • stackSizeMb <number> Максимальный размер стека потока по умолчанию. Небольшие значения могут сделать экземпляры Worker непригодными для использования. По умолчанию: 4.
    • name <string> Необязательное name, добавляемое к заголовку рабочего потока для отладки или идентификации; итоговый заголовок будет иметь вид [worker ${id}] ${name}. По умолчанию: ''.

Событие: 'error'

Добавлено в: v10.5.0
  • err <Error>

Событие 'error' возникает, если в рабочем потоке выбрасывается необработанное исключение. В этом случае рабочий поток завершается.

Событие: 'exit'

Добавлено в: v10.5.0
  • exitCode <integer>

Событие 'exit' возникает после остановки рабочего потока. Если рабочий поток завершился вызовом process.exit(), параметр exitCode содержит переданный код завершения. Если рабочий поток был принудительно остановлен, параметр exitCode имеет значение 1.

Это последнее событие, возникающее в любом экземпляре Worker.

Событие: 'message'

Добавлено в: v10.5.0
  • value <any> Переданное значение

Событие 'message' возникает, когда рабочий поток вызывает require('node:worker_threads').parentPort.postMessage(). Подробнее см. описание события port.on('message').

Все сообщения, отправленные из рабочего потока, доставляются до возникновения события 'exit' у объекта Worker.

Событие: 'messageerror'

Добавлено в: v14.5.0, v12.19.0
  • error <Error> Объект Error

Событие 'messageerror' возникает, если не удалось десериализовать сообщение.

Событие: 'online'

Добавлено в: v10.5.0

Событие 'online' возникает, когда рабочий поток начинает выполнять код JavaScript.

worker.getHeapSnapshot([options])

История
Версия Изменения
v19.1.0

Добавлена поддержка параметров настройки снимка кучи.

v13.9.0, v12.17.0

Добавлено в: v13.9.0, v12.17.0

  • options <Object>
    • exposeInternals <boolean> Если имеет значение true, внутренние данные включаются в снимок кучи. По умолчанию: false.
    • exposeNumericValues <boolean> Если имеет значение true, числовые значения включаются в искусственные поля. По умолчанию: false.
  • Возвращает: <Promise> Промис для читаемого потока, содержащего снимок кучи V8

Возвращает читаемый поток со снимком текущего состояния Worker в V8. Дополнительные сведения см. в разделе v8.getHeapSnapshot().

Если рабочий поток уже не выполняется — это может произойти до возникновения события 'exit', — возвращённый Promise немедленно отклоняется с ошибкой ERR_WORKER_NOT_RUNNING.

worker.performance

Добавлено в: v15.1.0, v14.17.0, v12.22.0

Объект, с помощью которого можно запрашивать сведения о производительности экземпляра рабочего потока. Аналогичен perf_hooks.performance.

performance.eventLoopUtilization([utilization1[, utilization2]])
Добавлено в: v15.1.0, v14.17.0, v12.22.0
  • utilization1 <Object> Результат предыдущего вызова eventLoopUtilization().
  • utilization2 <Object> Результат предыдущего вызова eventLoopUtilization() до utilization1.
  • Возвращает: <Object>
    • idle <number>
    • active <number>
    • utilization <number>

Выполняет тот же вызов, что и perf_hooks eventLoopUtilization(), но возвращает значения экземпляра рабочего потока.

Одно из отличий заключается в том, что, в отличие от главного потока, в рабочем потоке инициализация выполняется внутри цикла событий. Поэтому загрузка цикла событий доступна сразу после начала выполнения скрипта рабочего потока.

Отсутствие увеличения времени idle не означает, что рабочий поток завис при инициализации. В следующем примере за всё время работы рабочего потока не накапливается время idle, но при этом он может обрабатывать сообщения.

const { Worker, isMainThread, parentPort } = require('node:worker_threads');

if (isMainThread) {
  const worker = new Worker(__filename);
  setInterval(() => {
    worker.postMessage('hi');
    console.log(worker.performance.eventLoopUtilization());
  }, 100).unref();
  return;
}

parentPort.on('message', () => console.log('msg')).unref();
(function r(n) {
  if (--n < 0) return;
  const t = Date.now();
  while (Date.now() - t < 300);
  setImmediate(r, n);
})(10); copy

Загрузка цикла событий рабочего потока доступна только после возникновения события 'online'. Если вызвать метод до этого момента или после события 'exit', всем свойствам будет присвоено значение 0.

worker.postMessage(value[, transferList])

Добавлено в: v10.5.0
  • value <any>
  • transferList <Object[]>

Отправляет сообщение рабочему потоку, которое принимается через require('node:worker_threads').parentPort.on('message'). Дополнительные сведения см. в разделе port.postMessage().

worker.ref()

Добавлено в: v10.5.0

Противоположность unref(): вызов ref() для ранее unref() рабочего потока не позволяет программе завершиться, если он является единственным активным дескриптором (поведение по умолчанию). Если рабочий поток уже был ref(), повторный вызов ref() ничего не меняет.

worker.resourceLimits

Добавлено в: v13.2.0, v12.16.0
  • <Object>
    • maxYoungGenerationSizeMb <number>
    • maxOldGenerationSizeMb <number>
    • codeRangeSizeMb <number>
    • stackSizeMb <number>

Предоставляет набор ограничений ресурсов движка JS для этого рабочего потока. Если конструктору Worker был передан параметр resourceLimits, возвращаемые значения совпадают с ним.

Если рабочий поток остановлен, возвращается пустой объект.

worker.stderr

Добавлено в: v10.5.0
  • <stream.Readable>

Это читаемый поток, содержащий данные, записанные в process.stderr внутри рабочего потока. Если параметр stderr: true не был передан конструктору Worker, данные передаются в поток process.stderr родительского потока.

worker.stdin

Добавлено в: v10.5.0
  • <null> | <stream.Writable>

Если конструктору Worker был передан параметр stdin: true, это доступный для записи поток. Данные, записанные в этот поток, будут доступны в рабочем потоке как process.stdin.

worker.stdout

Добавлено в: v10.5.0
  • <stream.Readable>

Это читаемый поток, содержащий данные, записанные в process.stdout внутри рабочего потока. Если параметр stdout: true не был передан конструктору Worker, данные передаются в поток process.stdout родительского потока.

worker.terminate()

История
Версия Изменения
v12.5.0

Теперь эта функция возвращает Promise. Передача обратного вызова объявлена устаревшей; до этой версии она была бесполезна, поскольку Worker фактически завершался синхронно. Теперь завершение выполняется полностью асинхронно.

v10.5.0

Добавлено в: v10.5.0

  • Возвращает: <Promise>

Как можно скорее останавливает выполнение всего кода JavaScript в рабочем потоке. Возвращает Promise с кодом завершения, который выполняется при возникновении события 'exit'.

worker.threadId

Добавлено в: v10.5.0
  • <integer>

Числовой идентификатор соответствующего потока. Внутри рабочего потока он доступен как require('node:worker_threads').threadId. Это значение уникально для каждого экземпляра Worker в пределах одного процесса.

worker.unref()

Добавлено в: v10.5.0

Вызов unref() для рабочего потока позволяет завершить поток, если он является единственным активным дескриптором в системе событий. Если рабочий поток уже был unref(), повторный вызов unref() ничего не меняет.

Примечания

Синхронная блокировка стандартных потоков ввода-вывода

Объекты Worker используют передачу сообщений через <MessagePort> для взаимодействия с stdio. Это означает, что вывод stdio из Worker может блокироваться синхронным кодом на принимающей стороне, который блокирует цикл событий Node.js.

Модули JavaScript
import {
  Worker,
  isMainThread,
} from 'node:worker_threads';

if (isMainThread) {
  new Worker(new URL(import.meta.url));
  for (let n = 0; n < 1e10; n++) {
    // Looping to simulate work.
  }
} else {
  // This output will be blocked by the for loop in the main thread.
  console.log('foo');
}
CommonJS
'use strict';

const {
  Worker,
  isMainThread,
} = require('node:worker_threads');

if (isMainThread) {
  new Worker(__filename);
  for (let n = 0; n < 1e10; n++) {
    // Looping to simulate work.
  }
} else {
  // This output will be blocked by the for loop in the main thread.
  console.log('foo');
}

Запуск потоков Worker из предварительно загружаемых скриптов

Соблюдайте осторожность при запуске потоков Worker из предварительно загружаемых скриптов (скриптов, загружаемых и выполняемых с помощью флага командной строки -r). Если параметр execArgv не задан явно, новые потоки Worker автоматически наследуют флаги командной строки запущенного процесса и предварительно загружают те же скрипты, что и главный поток. Если скрипт предварительной загрузки безусловно запускает рабочий поток, каждый созданный поток будет создавать ещё один, пока приложение не завершится сбоем.

© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/dist/latest-v20.x/docs/api/worker_threads.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API