Рабочие потоки
Исходный код: 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)
-
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
Равно 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)
Помечает объект как непередаваемый. Если 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)
-
port<MessagePort> Порт сообщений для передачи. -
contextifiedSandbox<Object> Контекстуализированный объект, возвращённый методомvm.createContext(). -
Возвращает: <MessagePort>
Передаёт MessagePort в другой контекст vm. Исходный объект port становится непригодным для использования, и его заменяет возвращённый экземпляр MessagePort.
Возвращённый MessagePort является объектом в целевом контексте и наследуется от глобального класса Object этого контекста. Объекты, переданные обработчику port.onmessage(), также создаются в целевом контексте и наследуются от глобального класса Object этого контекста.
Однако созданный MessagePort больше не наследуется от EventTarget, и для получения событий с его помощью можно использовать только port.onmessage().
worker.parentPort
Если этот поток является потоком 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])
-
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)
-
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
Предоставляет набор ограничений на ресурсы движка JS в этом рабочем потоке. Если конструктору Worker был передан параметр resourceLimits, здесь будут указаны соответствующие значения.
При использовании в главном потоке возвращается пустой объект.
worker.SHARE_ENV
Специальное значение, которое можно передать как параметр 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])
-
key<any> Любое произвольное клонируемое значение JavaScript, которое можно использовать в качестве ключа <Map>. -
value<any> Любое произвольное клонируемое значение JavaScript, которое будет клонировано и автоматически передано всем новым экземплярамWorker. Если в качествеundefinedпереданоvalue, любое ранее заданное значение дляkeyбудет удалено.
API worker.setEnvironmentData() задаёт содержимое worker.getEnvironmentData() в текущем потоке и во всех новых экземплярах Worker, созданных из текущего контекста.
worker.threadId
Целочисленный идентификатор текущего потока. В соответствующем объекте рабочего потока (если он существует) это значение доступно как worker.threadId. Это значение уникально для каждого экземпляра Worker в рамках одного процесса.
worker.workerData
Произвольное значение 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
Экземпляры 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)
-
name<any> Имя канала для подключения. Допускается любое значение JavaScript, которое можно преобразовать в строку с помощью`${name}`.
broadcastChannel.close()
Закрывает соединение BroadcastChannel.
broadcastChannel.onmessage
- Тип: <Function> Вызывается с одним аргументом
MessageEventпри получении сообщения.
broadcastChannel.onmessageerror
- Тип: <Function> Вызывается, если полученное сообщение не удаётся десериализовать.
broadcastChannel.postMessage(message)
-
message<any> Любое клонируемое значение JavaScript.
broadcastChannel.ref()
Противоположность unref(). Вызов ref() для BroadcastChannel, ранее подвергнутого операции unref(), не позволяет программе завершиться, если это единственный оставшийся активный дескриптор (поведение по умолчанию). Если для порта была вызвана операция ref(), повторный вызов ref() не даст эффекта.
broadcastChannel.unref()
Вызов unref() для BroadcastChannel позволяет потоку завершиться, если это единственный активный дескриптор в системе событий. Если для BroadcastChannel уже была вызвана операция unref(), повторный вызов unref() не даст эффекта.
Класс: MessageChannel
Экземпляры класса 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
- Наследуется от: <EventTarget>
Экземпляры класса worker.MessagePort представляют один конец асинхронного двунаправленного канала связи. Его можно использовать для передачи структурированных данных, областей памяти и других MessagePort между разными Worker.
Эта реализация соответствует MessagePort браузера.
Событие: 'close'
Событие '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'
-
value<any> Переданное значение
Событие 'message' возникает при получении любого входящего сообщения и содержит клонированные входные данные, переданные в port.postMessage().
Обработчики этого события получают копию параметра value, переданного в postMessage(), без дополнительных аргументов.
Событие: 'messageerror'
-
error<Error> Объект Error
Событие 'messageerror' возникает при сбое десериализации сообщения.
В настоящее время это событие возникает при ошибке во время создания отправленного объекта JS на стороне получателя. Такие ситуации редки, но могут возникнуть, например, когда определенные объекты API Node.js получены в vm.Context (где API Node.js в настоящее время недоступны).
port.close()
Запрещает дальнейшую отправку сообщений с любой стороны соединения. Этот метод можно вызвать, если дальнейшее взаимодействие через этот MessagePort не предполагается.
Событие 'close' возникает для обоих экземпляров MessagePort, участвующих в канале.
port.postMessage(value[, transferList])
-
value<any> -
transferList<Object[]>
Отправляет значение JavaScript на принимающую сторону этого канала. value передается способом, совместимым с алгоритмом структурированного клонирования HTML.
В частности, существенные отличия от JSON заключаются в следующем:
-
valueможет содержать циклические ссылки. -
valueможет содержать экземпляры встроенных типов JS, таких какRegExp,BigInt,Map,Setи т. д. -
valueможет содержать типизированные массивы, использующие какArrayBuffer, так иSharedArrayBuffer. -
valueможет содержать экземплярыWebAssembly.Module. -
valueне может содержать объекты с нативной реализацией (на C++), за исключением:
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()
- Возвращает: <boolean>
Если значение равно true, объект MessagePort будет поддерживать активность цикла событий Node.js.
port.ref()
Противоположность unref(). Вызов ref() для порта, ранее вызвавшего unref(), не позволит программе завершиться, если это единственный оставшийся активный дескриптор (поведение по умолчанию). Если порт вызвал ref(), повторный вызов ref() не повлияет на результат.
При добавлении или удалении обработчиков с помощью .on('message') порт автоматически вызывает ref() или unref() в зависимости от наличия обработчиков события.
port.start()
Начинает получать сообщения через этот MessagePort. При использовании этого порта в качестве генератора событий метод вызывается автоматически после добавления обработчиков 'message'.
Этот метод существует для соответствия API Web MessagePort. В Node.js он полезен только для игнорирования сообщений, когда обработчик событий отсутствует. Также Node.js иначе обрабатывает .onmessage. Его установка автоматически вызывает .start(), но при удалении сообщения остаются в очереди, пока не будет задан новый обработчик или порт не будет удален.
port.unref()
Вызов unref() для порта позволяет потоку завершиться, если это единственный активный дескриптор в системе событий. Если порт уже вызвал unref(), повторный вызов unref() не повлияет на результат.
При добавлении или удалении обработчиков с помощью .on('message') порт автоматически вызывает ref() или unref() в зависимости от наличия обработчиков события.
Класс: Worker
- Расширяет: <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])
-
filename<string> | <URL> Путь к основному скрипту или модулю Worker. Должен быть абсолютным или относительным путём (то есть относительно текущего рабочего каталога), начинающимся с./или../, либо объектом WHATWGURL, использующим протокол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, как и другие ресурсы, например сетевые сокеты или файловые дескрипторы, управляемые через APIFileHandle. Этот параметр автоматически наследуется всеми вложенными объектами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'
-
err<Error>
Событие 'error' возникает, если в рабочем потоке выбрасывается необработанное исключение. В этом случае рабочий поток завершается.
Событие: 'exit'
-
exitCode<integer>
Событие 'exit' возникает после остановки рабочего потока. Если рабочий поток завершился вызовом process.exit(), параметр exitCode содержит переданный код завершения. Если рабочий поток был принудительно остановлен, параметр exitCode имеет значение 1.
Это последнее событие, возникающее в любом экземпляре Worker.
Событие: 'message'
-
value<any> Переданное значение
Событие 'message' возникает, когда рабочий поток вызывает require('node:worker_threads').parentPort.postMessage(). Подробнее см. описание события port.on('message').
Все сообщения, отправленные из рабочего потока, доставляются до возникновения события 'exit' у объекта Worker.
Событие: 'messageerror'
-
error<Error> Объект Error
Событие 'messageerror' возникает, если не удалось десериализовать сообщение.
Событие: 'online'
Событие 'online' возникает, когда рабочий поток начинает выполнять код JavaScript.
worker.getHeapSnapshot([options])
Возвращает читаемый поток со снимком текущего состояния Worker в V8. Дополнительные сведения см. в разделе v8.getHeapSnapshot().
Если рабочий поток уже не выполняется — это может произойти до возникновения события 'exit', — возвращённый Promise немедленно отклоняется с ошибкой ERR_WORKER_NOT_RUNNING.
worker.performance
Объект, с помощью которого можно запрашивать сведения о производительности экземпляра рабочего потока. Аналогичен perf_hooks.performance.
performance.eventLoopUtilization([utilization1[, utilization2]])
-
utilization1<Object> Результат предыдущего вызоваeventLoopUtilization(). -
utilization2<Object> Результат предыдущего вызоваeventLoopUtilization()доutilization1. - Возвращает: <Object>
Выполняет тот же вызов, что и 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])
-
value<any> -
transferList<Object[]>
Отправляет сообщение рабочему потоку, которое принимается через require('node:worker_threads').parentPort.on('message'). Дополнительные сведения см. в разделе port.postMessage().
worker.ref()
Противоположность unref(): вызов ref() для ранее unref() рабочего потока не позволяет программе завершиться, если он является единственным активным дескриптором (поведение по умолчанию). Если рабочий поток уже был ref(), повторный вызов ref() ничего не меняет.
worker.resourceLimits
Предоставляет набор ограничений ресурсов движка JS для этого рабочего потока. Если конструктору Worker был передан параметр resourceLimits, возвращаемые значения совпадают с ним.
Если рабочий поток остановлен, возвращается пустой объект.
worker.stderr
Это читаемый поток, содержащий данные, записанные в process.stderr внутри рабочего потока. Если параметр stderr: true не был передан конструктору Worker, данные передаются в поток process.stderr родительского потока.
worker.stdin
Если конструктору Worker был передан параметр stdin: true, это доступный для записи поток. Данные, записанные в этот поток, будут доступны в рабочем потоке как process.stdin.
worker.stdout
Это читаемый поток, содержащий данные, записанные в process.stdout внутри рабочего потока. Если параметр stdout: true не был передан конструктору Worker, данные передаются в поток process.stdout родительского потока.
worker.terminate()
- Возвращает: <Promise>
Как можно скорее останавливает выполнение всего кода JavaScript в рабочем потоке. Возвращает Promise с кодом завершения, который выполняется при возникновении события 'exit'.
worker.threadId
Числовой идентификатор соответствующего потока. Внутри рабочего потока он доступен как require('node:worker_threads').threadId. Это значение уникально для каждого экземпляра Worker в пределах одного процесса.
worker.unref()
Вызов 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