Hilos de trabajo (Worker threads)
[Estable: 2 - Estable]
Estable: 2 Estabilidad: 2 - Estable
Código fuente: lib/worker_threads.js
El módulo node:worker_threads
permite el uso de hilos que ejecutan JavaScript en paralelo. Para acceder a él:
const worker = require('node:worker_threads');
Los Workers (hilos) son útiles para realizar operaciones de JavaScript que consumen muchos recursos de CPU. No ayudan mucho con el trabajo intensivo de E/S. Las operaciones de E/S asíncronas integradas de Node.js son más eficientes de lo que pueden ser los Workers.
A diferencia de child_process
o cluster
, worker_threads
pueden compartir memoria. Lo hacen transfiriendo instancias de ArrayBuffer
o compartiendo instancias de 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));
}
El ejemplo anterior genera un hilo de Worker para cada llamada a parseJSAsync()
. En la práctica, utilice un pool de Workers para este tipo de tareas. De lo contrario, la sobrecarga de la creación de Workers probablemente superaría su beneficio.
Al implementar un pool de workers, utilice la API AsyncResource
para informar a las herramientas de diagnóstico (por ejemplo, para proporcionar trazas de pila asíncronas) sobre la correlación entre las tareas y sus resultados. Consulte "Usando AsyncResource
para un pool de hilos Worker
" en la documentación de async_hooks
para ver una implementación de ejemplo.
Los hilos de trabajo heredan las opciones no específicas del proceso de forma predeterminada. Consulte Opciones del constructor Worker
para saber cómo personalizar las opciones del hilo de trabajo, específicamente las opciones argv
y execArgv
.
worker.getEnvironmentData(key)
[Historial]
Versión | Cambios |
---|---|
v17.5.0, v16.15.0 | Ya no es experimental. |
v15.12.0, v14.18.0 | Añadido en: v15.12.0, v14.18.0 |
key
<any> Cualquier valor de JavaScript arbitrario y clonable que pueda ser usado como una clave <Map>.- Devuelve: <any>
Dentro de un hilo de trabajo, worker.getEnvironmentData()
devuelve un clon de los datos pasados a worker.setEnvironmentData()
del hilo de creación. Cada nuevo Worker
recibe su propia copia de los datos del entorno automáticamente.
const {
Worker,
isMainThread,
setEnvironmentData,
getEnvironmentData,
} = require('node:worker_threads');
if (isMainThread) {
setEnvironmentData('Hello', 'World!');
const worker = new Worker(__filename);
} else {
console.log(getEnvironmentData('Hello')); // Imprime 'World!'.
}
worker.isMainThread
Añadido en: v10.5.0
Es true
si este código no se está ejecutando dentro de un hilo Worker
.
const { Worker, isMainThread } = require('node:worker_threads');
if (isMainThread) {
// Esto vuelve a cargar el archivo actual dentro de una instancia Worker.
new Worker(__filename);
} else {
console.log('Inside Worker!');
console.log(isMainThread); // Imprime 'false'.
}
worker.markAsUntransferable(object)
Añadido en: v14.5.0, v12.19.0
object
<any> Cualquier valor de JavaScript arbitrario.
Marca un objeto como no transferible. Si object
aparece en la lista de transferencia de una llamada port.postMessage()
, se lanza un error. Esto es una operación nula si object
es un valor primitivo.
En particular, esto tiene sentido para objetos que pueden ser clonados, en lugar de transferidos, y que son utilizados por otros objetos en el lado remitente. Por ejemplo, Node.js marca los ArrayBuffer
s que utiliza para su Buffer
pool con esto.
Esta operación no se puede deshacer.
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();
try {
// Esto lanzará un error, porque pooledBuffer no es transferible.
port1.postMessage(typedArray1, [ typedArray1.buffer ]);
} catch (error) {
// error.name === 'DataCloneError'
}
// La siguiente línea imprime el contenido de typedArray1 -- todavía posee
// su memoria y no ha sido transferido. Sin
// `markAsUntransferable()`, esto imprimiría un Uint8Array vacío y la
// llamada a postMessage habría tenido éxito.
// typedArray2 también está intacto.
console.log(typedArray1);
console.log(typedArray2);
No existe un equivalente a esta API en los navegadores.
worker.isMarkedAsUntransferable(object)
Añadido en: v21.0.0
Comprueba si un objeto está marcado como no transferible con markAsUntransferable()
.
const { markAsUntransferable, isMarkedAsUntransferable } = require('node:worker_threads');
const pooledBuffer = new ArrayBuffer(8);
markAsUntransferable(pooledBuffer);
isMarkedAsUntransferable(pooledBuffer); // Devuelve true.
No existe un equivalente a esta API en los navegadores.
worker.markAsUncloneable(object)
Añadido en: v23.0.0
object
<any> Cualquier valor arbitrario de JavaScript.
Marca un objeto como no clonable. Si object
se utiliza como message
en una llamada a port.postMessage()
, se lanza un error. Esto no tiene efecto si object
es un valor primitivo.
Esto no tiene ningún efecto en ArrayBuffer
, ni en ningún objeto similar a Buffer
.
Esta operación no se puede deshacer.
const { markAsUncloneable } = require('node:worker_threads');
const anyObject = { foo: 'bar' };
markAsUncloneable(anyObject);
const { port1 } = new MessageChannel();
try {
// Esto lanzará un error, porque anyObject no es clonable.
port1.postMessage(anyObject);
} catch (error) {
// error.name === 'DataCloneError'
}
No existe un equivalente a esta API en los navegadores.
worker.moveMessagePortToContext(port, contextifiedSandbox)
Añadido en: v11.13.0
port
<MessagePort> El puerto de mensaje a transferir.contextifiedSandbox
<Object> Un objeto contextificado como el devuelto por el métodovm.createContext()
.- Devuelve: <MessagePort>
Transfiere un MessagePort
a un vm
Context diferente. El objeto port
original se vuelve inutilizable y la instancia MessagePort
devuelta lo reemplaza.
El MessagePort
devuelto es un objeto en el contexto de destino y hereda de su clase global Object
. Los objetos pasados al escuchador port.onmessage()
también se crean en el contexto de destino y heredan de su clase global Object
.
Sin embargo, el MessagePort
creado ya no hereda de EventTarget
, y solo port.onmessage()
se puede usar para recibir eventos usándolo.
worker.parentPort
Agregado en: v10.5.0
Si este hilo es un Worker
, este es un MessagePort
que permite la comunicación con el hilo padre. Los mensajes enviados utilizando parentPort.postMessage()
están disponibles en el hilo padre utilizando worker.on('message')
, y los mensajes enviados desde el hilo padre utilizando worker.postMessage()
están disponibles en este hilo utilizando 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); // Imprime '¡Hola, mundo!'.
});
worker.postMessage('¡Hola, mundo!');
} else {
// Cuando se recibe un mensaje del hilo padre, lo devuelve:
parentPort.once('message', (message) => {
parentPort.postMessage(message);
});
}
worker.postMessageToThread(threadId, value[, transferList][, timeout])
Agregado en: v22.5.0
[Estable: 1 - Experimental]
Estable: 1 Estabilidad: 1.1 - Desarrollo activo
threadId
<number> El ID del hilo de destino. Si el ID del hilo no es válido, se lanzará un errorERR_WORKER_MESSAGING_FAILED
. Si el ID del hilo de destino es el ID del hilo actual, se lanzará un errorERR_WORKER_MESSAGING_SAME_THREAD
.value
<any> El valor para enviar.transferList
<Object[]> Si se pasan uno o más objetos tipoMessagePort
envalue
, se requiere untransferList
para esos elementos o se lanzaERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST
. Consulteport.postMessage()
para obtener más información.timeout
<number> Tiempo para esperar a que el mensaje se entregue en milisegundos. De forma predeterminada esundefined
, lo que significa esperar para siempre. Si la operación agota el tiempo de espera, se lanza un errorERR_WORKER_MESSAGING_TIMEOUT
.- Devuelve: <Promise> Una promesa que se cumple si el mensaje fue procesado con éxito por el hilo de destino.
Envía un valor a otro worker, identificado por su ID de hilo.
Si el hilo de destino no tiene un listener para el evento workerMessage
, entonces la operación lanzará un error ERR_WORKER_MESSAGING_FAILED
.
Si el hilo de destino lanzó un error al procesar el evento workerMessage
, entonces la operación lanzará un error ERR_WORKER_MESSAGING_ERRORED
.
Este método debe usarse cuando el hilo de destino no es el padre o hijo directo del hilo actual. Si los dos hilos son padre-hijos, use require('node:worker_threads').parentPort.postMessage()
y worker.postMessage()
para permitir que los hilos se comuniquen.
El ejemplo a continuación muestra el uso de postMessageToThread
: crea 10 hilos anidados, el último intentará comunicarse con el hilo principal.
import { fileURLToPath } from 'node:url';
import process from 'node:process';
import {
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;
const {
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)
[Historial]
Versión | Cambios |
---|---|
v15.12.0 | El argumento port ahora también puede referirse a un BroadcastChannel . |
v12.3.0 | Añadido en: v12.3.0 |
port
<MessagePort> | <BroadcastChannel>- Devuelve: <Object> | <undefined>
Recibe un solo mensaje de un MessagePort
dado. Si no hay ningún mensaje disponible, se devuelve undefined
, de lo contrario, un objeto con una sola propiedad message
que contiene la carga útil del mensaje, correspondiente al mensaje más antiguo en la cola del MessagePort
.
const { MessageChannel, receiveMessageOnPort } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();
port1.postMessage({ hello: 'world' });
console.log(receiveMessageOnPort(port2));
// Imprime: { message: { hello: 'world' } }
console.log(receiveMessageOnPort(port2));
// Imprime: undefined
Cuando se utiliza esta función, no se emite ningún evento 'message'
y no se invoca el listener onmessage
.
worker.resourceLimits
Añadido en: v13.2.0, v12.16.0
Proporciona el conjunto de restricciones de recursos del motor JS dentro de este hilo Worker. Si la opción resourceLimits
se pasó al constructor Worker
, esto coincide con sus valores.
Si esto se utiliza en el hilo principal, su valor es un objeto vacío.
worker.SHARE_ENV
Agregado en: v11.14.0
Un valor especial que se puede pasar como la opción env
del constructor Worker
, para indicar que el hilo actual y el hilo Worker deben compartir acceso de lectura y escritura al mismo conjunto de variables de entorno.
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); // Imprime 'foo'.
});
worker.setEnvironmentData(key[, value])
[Historial]
Versión | Cambios |
---|---|
v17.5.0, v16.15.0 | Ya no es experimental. |
v15.12.0, v14.18.0 | Agregado en: v15.12.0, v14.18.0 |
key
<any> Cualquier valor JavaScript arbitrario y clonable que pueda usarse como una clave de <Map>.value
<any> Cualquier valor JavaScript arbitrario y clonable que se clonará y pasará automáticamente a todas las nuevas instancias deWorker
. Sivalue
se pasa comoundefined
, cualquier valor establecido previamente para lakey
se eliminará.
La API worker.setEnvironmentData()
establece el contenido de worker.getEnvironmentData()
en el hilo actual y en todas las nuevas instancias de Worker
generadas desde el contexto actual.
worker.threadId
Agregado en: v10.5.0
Un identificador entero para el hilo actual. En el objeto de trabajo correspondiente (si lo hay), está disponible como worker.threadId
. Este valor es único para cada instancia de Worker
dentro de un único proceso.
worker.workerData
Agregado en: v10.5.0
Un valor arbitrario de JavaScript que contiene un clon de los datos pasados al constructor Worker
de este hilo.
Los datos se clonan como si se utilizara postMessage()
, de acuerdo con el algoritmo de clonación estructurada de HTML.
const { Worker, isMainThread, workerData } = require('node:worker_threads');
if (isMainThread) {
const worker = new Worker(__filename, { workerData: 'Hello, world!' });
} else {
console.log(workerData); // Imprime 'Hello, world!'.
}
Clase: BroadcastChannel extends EventTarget
[Historial]
Versión | Cambios |
---|---|
v18.0.0 | Ya no es experimental. |
v15.4.0 | Agregado en: v15.4.0 |
Las instancias de BroadcastChannel
permiten la comunicación asíncrona de uno a muchos con todas las demás instancias de BroadcastChannel
vinculadas al mismo nombre de canal.
'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();
}
new BroadcastChannel(name)
Agregado en: v15.4.0
name
<any> El nombre del canal al que conectarse. Se permite cualquier valor de JavaScript que se pueda convertir en una cadena usando${name}
.
broadcastChannel.close()
Agregado en: v15.4.0
Cierra la conexión BroadcastChannel
.
broadcastChannel.onmessage
Agregado en: v15.4.0
- Tipo: <Function> Se invoca con un solo argumento
MessageEvent
cuando se recibe un mensaje.
broadcastChannel.onmessageerror
Agregado en: v15.4.0
- Tipo: <Function> Se invoca cuando un mensaje recibido no puede ser deserializado.
broadcastChannel.postMessage(message)
Agregado en: v15.4.0
message
<any> Cualquier valor de JavaScript que se pueda clonar.
broadcastChannel.ref()
Agregado en: v15.4.0
Opuesto a unref()
. Llamar a ref()
en un BroadcastChannel previamente unref()
ed no permite que el programa se cierre si es el único controlador activo que queda (el comportamiento predeterminado). Si el puerto está ref()
ed, llamar a ref()
de nuevo no tiene ningún efecto.
broadcastChannel.unref()
Agregado en: v15.4.0
Llamar a unref()
en un BroadcastChannel permite que el hilo se cierre si este es el único controlador activo en el sistema de eventos. Si el BroadcastChannel ya está unref()
ed, llamar a unref()
de nuevo no tiene ningún efecto.
Clase: MessageChannel
Agregado en: v10.5.0
Las instancias de la clase worker.MessageChannel
representan un canal de comunicación bidireccional asíncrono. MessageChannel
no tiene métodos propios. new MessageChannel()
produce un objeto con propiedades port1
y port2
, que hacen referencia a instancias de MessagePort
enlazadas.
const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();
port1.on('message', (message) => console.log('received', message));
port2.postMessage({ foo: 'bar' });
// Imprime: received { foo: 'bar' } desde el listener `port1.on('message')`
Clase: MessagePort
[Historial]
Versión | Cambios |
---|---|
v14.7.0 | Esta clase ahora hereda de EventTarget en lugar de EventEmitter . |
v10.5.0 | Agregado en: v10.5.0 |
- Extiende: <EventTarget>
Las instancias de la clase worker.MessagePort
representan un extremo de un canal de comunicación bidireccional asíncrono. Se puede utilizar para transferir datos estructurados, regiones de memoria y otros MessagePort
s entre diferentes Worker
s.
Esta implementación coincide con MessagePort
s del navegador.
Evento: 'close'
Añadido en: v10.5.0
El evento 'close'
se emite una vez que cualquiera de los lados del canal se ha desconectado.
const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();
// Imprime:
// foobar
// ¡cerrado!
port2.on('message', (message) => console.log(message));
port2.on('close', () => console.log('¡cerrado!'));
port1.postMessage('foobar');
port1.close();
Evento: 'message'
Añadido en: v10.5.0
value
<any> El valor transmitido
El evento 'message'
se emite para cualquier mensaje entrante, que contiene la entrada clonada de port.postMessage()
.
Los listeners de este evento reciben un clon del parámetro value
tal como se pasa a postMessage()
y ningún argumento adicional.
Evento: 'messageerror'
Añadido en: v14.5.0, v12.19.0
error
<Error> Un objeto Error
El evento 'messageerror'
se emite cuando falla la deserialización de un mensaje.
Actualmente, este evento se emite cuando se produce un error al instanciar el objeto JS publicado en el extremo receptor. Estas situaciones son raras, pero pueden ocurrir, por ejemplo, cuando ciertos objetos API de Node.js se reciben en un vm.Context
(donde las API de Node.js no están disponibles actualmente).
port.close()
Añadido en: v10.5.0
Desactiva el envío posterior de mensajes a ambos lados de la conexión. Este método se puede llamar cuando no se vaya a producir más comunicación a través de este MessagePort
.
El evento 'close'
se emite en ambas instancias de MessagePort
que forman parte del canal.
port.postMessage(value[, transferList])
[Historial]
Versión | Cambios |
---|---|
v21.0.0 | Se lanza un error cuando hay un objeto no transferible en la lista de transferencia. |
v15.6.0 | Se añadió X509Certificate a la lista de tipos clonables. |
v15.0.0 | Se añadió CryptoKey a la lista de tipos clonables. |
v15.14.0, v14.18.0 | Se añadió 'BlockList' a la lista de tipos clonables. |
v15.9.0, v14.18.0 | Se añadieron tipos 'Histogram' a la lista de tipos clonables. |
v14.5.0, v12.19.0 | Se añadió KeyObject a la lista de tipos clonables. |
v14.5.0, v12.19.0 | Se añadió FileHandle a la lista de tipos transferibles. |
v10.5.0 | Añadido en: v10.5.0 |
value
<any>transferList
<Object[]>
Envía un valor de JavaScript al lado receptor de este canal. value
se transfiere de una manera que es compatible con el algoritmo de clonación estructurada HTML.
En particular, las diferencias significativas con JSON
son:
value
puede contener referencias circulares.value
puede contener instancias de tipos JS integrados comoRegExp
s,BigInt
s,Map
s,Set
s, etc.value
puede contener matrices tipadas, tanto utilizandoArrayBuffer
s comoSharedArrayBuffer
s.value
puede contener instancias deWebAssembly.Module
.value
no puede contener objetos nativos (respaldados por C++) que no sean:
const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();
port1.on('message', (message) => console.log(message));
const circularData = {};
circularData.foo = circularData;
// Imprime: { foo: [Circular] }
port2.postMessage(circularData);
transferList
puede ser una lista de objetos ArrayBuffer
, MessagePort
y FileHandle
. Después de la transferencia, ya no son utilizables en el lado remitente del canal (incluso si no están contenidos en value
). A diferencia de los procesos secundarios, actualmente no se admite la transferencia de handles como sockets de red.
Si value
contiene instancias de SharedArrayBuffer
, se puede acceder a ellas desde cualquier hilo. No se pueden enumerar en transferList
.
value
todavía puede contener instancias de ArrayBuffer
que no están en transferList
; en ese caso, la memoria subyacente se copia en lugar de moverse.
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 ]);
// Esto publica una copia de `uint8Array`:
port2.postMessage(uint8Array);
// Esto no copia los datos, pero hace que `uint8Array` sea inutilizable:
port2.postMessage(uint8Array, [ uint8Array.buffer ]);
// La memoria para el `sharedUint8Array` es accesible tanto desde el
// original como de la copia recibida por `.on('message')`:
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4));
port2.postMessage(sharedUint8Array);
// Esto transfiere un puerto de mensajes recién creado al receptor.
// Esto se puede utilizar, por ejemplo, para crear canales de comunicación entre
// múltiples hilos `Worker` que son hijos del mismo hilo padre.
const otherChannel = new MessageChannel();
port2.postMessage({ port: otherChannel.port1 }, [ otherChannel.port1 ]);
El objeto de mensaje se clona inmediatamente y se puede modificar después de la publicación sin tener efectos secundarios.
Para obtener más información sobre los mecanismos de serialización y deserialización detrás de esta API, consulte la API de serialización del módulo node:v8
.
Consideraciones al transferir TypedArrays y Buffers
Todas las instancias de TypedArray
y Buffer
son vistas sobre un ArrayBuffer
subyacente. Es decir, es el ArrayBuffer
el que realmente almacena los datos brutos, mientras que los objetos TypedArray
y Buffer
proporcionan una forma de ver y manipular los datos. Es posible y común que se creen múltiples vistas sobre la misma instancia de ArrayBuffer
. Se debe tener mucho cuidado al usar una lista de transferencia para transferir un ArrayBuffer
, ya que hacerlo hace que todas las instancias de TypedArray
y Buffer
que comparten el mismo ArrayBuffer
se vuelvan inutilizables.
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
Para las instancias de Buffer
, específicamente, si el ArrayBuffer
subyacente se puede transferir o clonar depende completamente de cómo se crearon las instancias, lo que a menudo no se puede determinar de manera confiable.
Un ArrayBuffer
puede marcarse con markAsUntransferable()
para indicar que siempre debe clonarse y nunca transferirse.
Dependiendo de cómo se creó una instancia de Buffer
, puede o no ser propietaria de su ArrayBuffer
subyacente. Un ArrayBuffer
no debe transferirse a menos que se sepa que la instancia de Buffer
es propietaria de él. En particular, para los Buffer
creados a partir del pool interno de Buffer
(usando, por ejemplo, Buffer.from()
o Buffer.allocUnsafe()
), transferirlos no es posible y siempre se clonan, lo que envía una copia de todo el pool de Buffer
. Este comportamiento puede conllevar un mayor uso de memoria no deseado y posibles problemas de seguridad.
Consulte Buffer.allocUnsafe()
para obtener más detalles sobre el pooling de Buffer
.
Los ArrayBuffer
para las instancias de Buffer
creadas usando Buffer.alloc()
o Buffer.allocUnsafeSlow()
siempre se pueden transferir, pero hacerlo inutiliza todas las demás vistas existentes de esos ArrayBuffer
.
Consideraciones al clonar objetos con prototipos, clases y accesores
Debido a que la clonación de objetos utiliza el algoritmo de clonación estructurada HTML, las propiedades no enumerables, los accesores de propiedades y los prototipos de objetos no se conservan. En particular, los objetos Buffer
se leerán como Uint8Array
s simples en el lado receptor, y las instancias de clases JavaScript se clonarán como objetos JavaScript simples.
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 }
Esta limitación se extiende a muchos objetos incorporados, como el objeto global URL
:
const { port1, port2 } = new MessageChannel();
port1.onmessage = ({ data }) => console.log(data);
port2.postMessage(new URL('https://example.org'));
// Prints: { }
port.hasRef()
Agregado en: v18.1.0, v16.17.0
[Estable: 1 - Experimental]
Estable: 1 Estabilidad: 1 - Experimental
- Devuelve: <boolean>
Si es verdadero, el objeto MessagePort
mantendrá activo el bucle de eventos de Node.js.
port.ref()
Agregado en: v10.5.0
Opuesto de unref()
. Llamar a ref()
en un puerto previamente unref()
ed no permite que el programa salga si es el único controlador activo restante (el comportamiento predeterminado). Si el puerto está ref()
ed, llamar a ref()
nuevamente no tiene ningún efecto.
Si los listeners se adjuntan o eliminan usando .on('message')
, el puerto se ref()
ed y unref()
ed automáticamente dependiendo de si existen listeners para el evento.
port.start()
Agregado en: v10.5.0
Comienza a recibir mensajes en este MessagePort
. Cuando se usa este puerto como un emisor de eventos, esto se llama automáticamente una vez que se adjuntan los listeners 'message'
.
Este método existe para la paridad con la API Web MessagePort
. En Node.js, solo es útil para ignorar mensajes cuando no hay un listener de eventos presente. Node.js también difiere en su manejo de .onmessage
. Establecerlo llama automáticamente a .start()
, pero desactivarlo permite que los mensajes se pongan en cola hasta que se establezca un nuevo controlador o se descarte el puerto.
port.unref()
Agregado en: v10.5.0
Llamar a unref()
en un puerto permite que el hilo se cierre si este es el único controlador activo en el sistema de eventos. Si el puerto ya está unref()
ed, llamar a unref()
nuevamente no tiene ningún efecto.
Si los listeners se adjuntan o eliminan usando .on('message')
, el puerto se ref()
ed y unref()
ed automáticamente dependiendo de si existen listeners para el evento.
Clase: Worker
Agregado en: v10.5.0
- Extiende: <EventEmitter>
La clase Worker
representa un hilo de ejecución de JavaScript independiente. La mayoría de las API de Node.js están disponibles dentro de él.
Las diferencias notables dentro de un entorno Worker son:
- Los streams
process.stdin
,process.stdout
yprocess.stderr
pueden ser redirigidos por el hilo principal. - La propiedad
require('node:worker_threads').isMainThread
se establece enfalse
. - El puerto de mensajes
require('node:worker_threads').parentPort
está disponible. process.exit()
no detiene todo el programa, solo el hilo único, yprocess.abort()
no está disponible.process.chdir()
y los métodosprocess
que establecen identificadores de grupo o usuario no están disponibles.process.env
es una copia de las variables de entorno del hilo principal, a menos que se especifique lo contrario. Los cambios en una copia no son visibles en otros hilos y no son visibles para los complementos nativos (a menos queworker.SHARE_ENV
se pase como la opciónenv
al constructorWorker
). En Windows, a diferencia del hilo principal, una copia de las variables de entorno funciona de manera sensible a mayúsculas y minúsculas.process.title
no se puede modificar.- Las señales no se entregan a través de
process.on('...')
. - La ejecución puede detenerse en cualquier momento como resultado de la invocación de
worker.terminate()
. - Los canales IPC de los procesos padre no son accesibles.
- El módulo
trace_events
no es compatible. - Los complementos nativos solo se pueden cargar desde múltiples hilos si cumplen ciertas condiciones.
Es posible crear instancias de Worker
dentro de otros Worker
s.
Al igual que los Web Workers y el node:cluster
module, la comunicación bidireccional se puede lograr a través del paso de mensajes entre hilos. Internamente, un Worker
tiene un par incorporado de MessagePort
s que ya están asociados entre sí cuando se crea el Worker
. Si bien el objeto MessagePort
en el lado principal no está directamente expuesto, sus funcionalidades se exponen a través de worker.postMessage()
y el evento worker.on('message')
en el objeto Worker
para el hilo principal.
Para crear canales de mensajería personalizados (lo que se recomienda en lugar de usar el canal global predeterminado porque facilita la separación de preocupaciones), los usuarios pueden crear un objeto MessageChannel
en cualquier hilo y pasar uno de los MessagePort
s en ese MessageChannel
al otro hilo a través de un canal preexistente, como el global.
Consulte port.postMessage()
para obtener más información sobre cómo se pasan los mensajes y qué tipo de valores de JavaScript se pueden transportar con éxito a través de la barrera del hilo.
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();
});
}
new Worker(filename[, options])
[Historial]
Versión | Cambios |
---|---|
v19.8.0, v18.16.0 | Se agregó soporte para una opción name , que permite agregar un nombre al título del worker para depuración. |
v14.9.0 | El parámetro filename puede ser un objeto URL WHATWG que usa el protocolo data: . |
v14.9.0 | La opción trackUnmanagedFds se estableció en true de forma predeterminada. |
v14.6.0, v12.19.0 | Se introdujo la opción trackUnmanagedFds . |
v13.13.0, v12.17.0 | Se introdujo la opción transferList . |
v13.12.0, v12.17.0 | El parámetro filename puede ser un objeto URL WHATWG que usa el protocolo file: . |
v13.4.0, v12.16.0 | Se introdujo la opción argv . |
v13.2.0, v12.16.0 | Se introdujo la opción resourceLimits . |
v10.5.0 | Se agregó en: v10.5.0 |
filename
<string> | <URL> La ruta al script principal o módulo del Worker. Debe ser una ruta absoluta o una ruta relativa (es decir, relativa al directorio de trabajo actual) que comience con./
o../
, o un objetoURL
WHATWG que use el protocolofile:
odata:
. Cuando se usa unadata:
URL, los datos se interpretan según el tipo MIME usando el cargador de módulos ECMAScript. Sioptions.eval
estrue
, esto es una cadena que contiene código JavaScript en lugar de una ruta.options
<Object>argv
<any[]> Lista de argumentos que se convertirían en cadena y se agregarían aprocess.argv
en el worker. Esto es principalmente similar aworkerData
, pero los valores están disponibles en elprocess.argv
global como si se pasaran como opciones de CLI al script.env
<Object> Si se establece, especifica el valor inicial deprocess.env
dentro del hilo Worker. Como valor especial, se puede usarworker.SHARE_ENV
para especificar que el hilo principal y el hilo secundario deben compartir sus variables de entorno; en ese caso, los cambios en el objetoprocess.env
de un hilo también afectan al otro hilo. Predeterminado:process.env
.eval
<boolean> Si estrue
y el primer argumento es unastring
, interpreta el primer argumento al constructor como un script que se ejecuta una vez que el worker está en línea.execArgv
<string[]> Lista de opciones de CLI de node pasadas al worker. Las opciones de V8 (como--max-old-space-size
) y las opciones que afectan el proceso (como--title
) no son compatibles. Si se establece, esto se proporciona comoprocess.execArgv
dentro del worker. De forma predeterminada, las opciones se heredan del hilo principal.stdin
<boolean> Si esto se establece entrue
, entoncesworker.stdin
proporciona un flujo de escritura cuyo contenido aparece comoprocess.stdin
dentro del Worker. De forma predeterminada, no se proporcionan datos.stdout
<boolean> Si esto se establece entrue
, entoncesworker.stdout
no se canaliza automáticamente aprocess.stdout
en el padre.stderr
<boolean> Si esto se establece entrue
, entoncesworker.stderr
no se canaliza automáticamente aprocess.stderr
en el padre.workerData
<any> Cualquier valor de JavaScript que se clona y se pone a disposición comorequire('node:worker_threads').workerData
. La clonación se produce como se describe en el algoritmo de clonación estructurada HTML, y se produce un error si el objeto no se puede clonar (por ejemplo, porque contienefunction
s).trackUnmanagedFds
<boolean> Si esto se establece entrue
, entonces el Worker rastrea los descriptores de archivo sin administrar gestionados a través defs.open()
yfs.close()
, y los cierra cuando el Worker sale, de forma similar a otros recursos como sockets de red o descriptores de archivo gestionados a través de la APIFileHandle
. Esta opción se hereda automáticamente de todos losWorker
s anidados. Predeterminado:true
.transferList
<Object[]> Si se pasan uno o más objetos similares aMessagePort
enworkerData
, se requiere unatransferList
para esos elementos o se lanzaERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST
. Consulteport.postMessage()
para obtener más información.resourceLimits
<Object> Un conjunto opcional de límites de recursos para la nueva instancia del motor JS. Alcanzar estos límites conduce a la finalización de la instanciaWorker
. Estos límites solo afectan al motor JS, y a ningún dato externo, incluyendo ningúnArrayBuffer
. Incluso si se establecen estos límites, el proceso aún puede abortar si encuentra una situación global de falta de memoria.maxOldGenerationSizeMb
<number> El tamaño máximo del montón principal en MB. Si se establece el argumento de la línea de comandos--max-old-space-size
, este valor se anula.maxYoungGenerationSizeMb
<number> El tamaño máximo de un espacio de montón para objetos creados recientemente. Si se establece el argumento de la línea de comandos--max-semi-space-size
, este valor se anula.codeRangeSizeMb
<number> El tamaño de un rango de memoria preasignado utilizado para el código generado.stackSizeMb
<number> El tamaño máximo de pila predeterminado para el hilo. Los valores pequeños pueden conducir a instancias de Worker inutilizables. Predeterminado:4
.name
<string> Unname
opcional que se agregará al título del worker para fines de depuración/identificación, haciendo que el título final sea[worker ${id}] ${name}
. Predeterminado:''
.
Evento: 'error'
Añadido en: v10.5.0
err
<Error>
El evento 'error'
se emite si el hilo de trabajo lanza una excepción no capturada. En ese caso, el trabajador se termina.
Evento: 'exit'
Añadido en: v10.5.0
exitCode
<integer>
El evento 'exit'
se emite una vez que el trabajador se ha detenido. Si el trabajador salió llamando a process.exit()
, el parámetro exitCode
es el código de salida pasado. Si el trabajador fue terminado, el parámetro exitCode
es 1
.
Este es el evento final emitido por cualquier instancia de Worker
.
Evento: 'message'
Añadido en: v10.5.0
value
<any> El valor transmitido
El evento 'message'
se emite cuando el hilo de trabajo ha invocado require('node:worker_threads').parentPort.postMessage()
. Consulte el evento port.on('message')
para obtener más detalles.
Todos los mensajes enviados desde el hilo de trabajo se emiten antes de que se emita el 'exit'
event en el objeto Worker
.
Evento: 'messageerror'
Añadido en: v14.5.0, v12.19.0
error
<Error> Un objeto Error
El evento 'messageerror'
se emite cuando falla la deserialización de un mensaje.
Evento: 'online'
Añadido en: v10.5.0
El evento 'online'
se emite cuando el hilo de trabajo ha comenzado a ejecutar código JavaScript.
worker.getHeapSnapshot([options])
[Historial]
Versión | Cambios |
---|---|
v19.1.0 | Soporte para opciones para configurar la instantánea de montón. |
v13.9.0, v12.17.0 | Añadido en: v13.9.0, v12.17.0 |
options
<Object>Devuelve: <Promise> Una promesa para un flujo Readable que contiene una instantánea de montón V8
Devuelve un flujo legible para una instantánea V8 del estado actual del Worker. Consulte v8.getHeapSnapshot()
para obtener más detalles.
Si el hilo de Worker ya no se está ejecutando, lo que puede ocurrir antes de que se emita el evento 'exit'
event, la Promise
devuelta se rechaza inmediatamente con un error ERR_WORKER_NOT_RUNNING
.
worker.performance
Agregado en: v15.1.0, v14.17.0, v12.22.0
Un objeto que se puede usar para consultar información de rendimiento de una instancia de worker. Similar a perf_hooks.performance
.
performance.eventLoopUtilization([utilization1[, utilization2]])
Agregado en: v15.1.0, v14.17.0, v12.22.0
utilization1
<Object> El resultado de una llamada anterior aeventLoopUtilization()
.utilization2
<Object> El resultado de una llamada anterior aeventLoopUtilization()
anterior autilization1
.- Devuelve: <Object>
La misma llamada que perf_hooks
eventLoopUtilization()
, excepto que se devuelven los valores de la instancia del worker.
Una diferencia es que, a diferencia del hilo principal, el bootstrapping dentro de un worker se realiza dentro del bucle de eventos. Por lo tanto, la utilización del bucle de eventos está disponible inmediatamente una vez que el script del worker comienza la ejecución.
Un tiempo idle
que no aumenta no indica que el worker está atascado en el bootstrap. Los siguientes ejemplos muestran cómo toda la vida útil del worker nunca acumula tiempo idle
, pero aún puede procesar mensajes.
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);
La utilización del bucle de eventos de un worker está disponible solo después de que se emite el evento 'online'
, y si se llama antes de esto, o después del evento 'exit'
, entonces todas las propiedades tienen el valor de 0
.
worker.postMessage(value[, transferList])
Añadido en: v10.5.0
value
<any>transferList
<Object[]>
Envía un mensaje al worker que se recibe a través de require('node:worker_threads').parentPort.on('message')
. Consulte port.postMessage()
para obtener más detalles.
worker.ref()
Añadido en: v10.5.0
Opuesto a unref()
, llamar a ref()
en un worker previamente unref()
ed no permite que el programa se cierre si es el único controlador activo que queda (el comportamiento predeterminado). Si el worker es ref()
ed, llamar a ref()
de nuevo no tiene ningún efecto.
worker.resourceLimits
Añadido en: v13.2.0, v12.16.0
Proporciona el conjunto de restricciones de recursos del motor JS para este hilo de Worker. Si la opción resourceLimits
se pasó al constructor Worker
, esto coincide con sus valores.
Si el worker se ha detenido, el valor de retorno es un objeto vacío.
worker.stderr
Añadido en: v10.5.0
Este es un flujo legible que contiene datos escritos en process.stderr
dentro del hilo del worker. Si stderr: true
no se pasó al constructor Worker
, entonces los datos se canalizan al flujo process.stderr
del hilo principal.
worker.stdin
Agregado en: v10.5.0
Si se pasó stdin: true
al constructor Worker
, este es un flujo de escritura. Los datos escritos en este flujo estarán disponibles en el hilo del trabajador como process.stdin
.
worker.stdout
Agregado en: v10.5.0
Este es un flujo de lectura que contiene datos escritos en process.stdout
dentro del hilo del trabajador. Si stdout: true
no se pasó al constructor Worker
, entonces los datos se canalizan al flujo process.stdout
del hilo principal.
worker.terminate()
[Historial]
Versión | Cambios |
---|---|
v12.5.0 | Esta función ahora devuelve una Promise. Pasar una función de callback está en desuso, y fue inútil hasta esta versión, ya que el Worker se terminaba de forma síncrona. Terminar ahora es una operación totalmente asíncrona. |
v10.5.0 | Agregado en: v10.5.0 |
- Devuelve: <Promise>
Detiene toda la ejecución de JavaScript en el hilo del trabajador lo antes posible. Devuelve una Promise para el código de salida que se cumple cuando se emite el 'exit'
event.
worker.threadId
Agregado en: v10.5.0
Un identificador entero para el hilo referenciado. Dentro del hilo del trabajador, está disponible como require('node:worker_threads').threadId
. Este valor es único para cada instancia de Worker
dentro de un solo proceso.
worker.unref()
Agregado en: v10.5.0
Llamar a unref()
en un trabajador permite que el hilo se cierre si este es el único controlador activo en el sistema de eventos. Si el trabajador ya está unref()
ed, llamar a unref()
nuevamente no tiene ningún efecto.
Notas
Bloqueo síncrono de stdio
Los Worker
s utilizan el paso de mensajes a través de <MessagePort> para implementar las interacciones con stdio
. Esto significa que la salida de stdio
originada en un Worker
puede ser bloqueada por código síncrono en el extremo receptor que esté bloqueando el bucle de eventos de Node.js.
import {
Worker,
isMainThread,
} from 'node:worker_threads';
if (isMainThread) {
new Worker(new URL(import.meta.url));
for (let n = 0; n < 1e10; n++) {
// Bucle para simular trabajo.
}
} else {
// Esta salida será bloqueada por el bucle for en el hilo principal.
console.log('foo');
}
'use strict';
const {
Worker,
isMainThread,
} = require('node:worker_threads');
if (isMainThread) {
new Worker(__filename);
for (let n = 0; n < 1e10; n++) {
// Bucle para simular trabajo.
}
} else {
// Esta salida será bloqueada por el bucle for en el hilo principal.
console.log('foo');
}
Lanzamiento de hilos de trabajo desde scripts de precarga
Tenga cuidado al lanzar hilos de trabajo desde scripts de precarga (scripts cargados y ejecutados utilizando el indicador de línea de comandos -r
). A menos que la opción execArgv
esté explícitamente establecida, los nuevos hilos de Worker heredan automáticamente los indicadores de línea de comandos del proceso en ejecución y precargarán los mismos scripts de precarga que el hilo principal. Si el script de precarga lanza incondicionalmente un hilo de trabajo, cada hilo generado generará otro hasta que la aplicación falle.