Skip to content

Hilos de trabajo

[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:

js
const worker = require('node:worker_threads')

Los workers (hilos) son útiles para realizar operaciones de JavaScript intensivas en CPU. No ayudan mucho con el trabajo intensivo en I/O. 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 puede compartir memoria. Lo hacen transfiriendo instancias de ArrayBuffer o compartiendo instancias de SharedArrayBuffer.

js
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 se detuvo con código de salida ${code}`))
      })
    })
  }
} else {
  const { parse } = require('some-js-parsing-library')
  const script = workerData
  parentPort.postMessage(parse(script))
}

El ejemplo anterior genera un hilo Worker para cada llamada a parseJSAsync(). En la práctica, utilice un grupo de Workers para este tipo de tareas. De lo contrario, la sobrecarga de crear Workers probablemente excedería su beneficio.

Al implementar un grupo de trabajadores, utilice la API AsyncResource para informar a las herramientas de diagnóstico (por ejemplo, para proporcionar seguimientos de pila asíncronos) sobre la correlación entre las tareas y sus resultados. Consulte "Uso de AsyncResource para un grupo de hilos Worker" en la documentación de async_hooks para ver un ejemplo de implementación.

Los hilos Worker 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 Worker, específicamente las opciones argv y execArgv.

worker.getEnvironmentData(key)

[Historial]

VersiónCambios
v17.5.0, v16.15.0Ya no es experimental.
v15.12.0, v14.18.0Añadido en: v15.12.0, v14.18.0
  • key <any> Cualquier valor JavaScript arbitrario y clonable que pueda usarse como clave de un <Map>.
  • Devuelve: <any>

Dentro de un hilo de worker, worker.getEnvironmentData() devuelve un clon de los datos pasados a worker.setEnvironmentData() del hilo de origen. Cada nuevo Worker recibe su propia copia de los datos de entorno automáticamente.

js
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

Agregado en: v10.5.0

Es true si este código no se está ejecutando dentro de un hilo Worker.

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

if (isMainThread) {
  // Esto vuelve a cargar el archivo actual dentro de una instancia de Worker.
  new Worker(__filename)
} else {
  console.log('¡Dentro del Worker!')
  console.log(isMainThread) // Imprime 'false'.
}

worker.markAsUntransferable(object)

Agregado en: v14.5.0, v12.19.0

  • object <any> Cualquier valor arbitrario de JavaScript.

Marca un objeto como no transferible. Si object aparece en la lista de transferencia de una llamada a port.postMessage(), se genera un error. Esta es una operación no operativa si object es un valor primitivo.

En particular, esto tiene sentido para objetos que se pueden clonar, en lugar de transferir, y que son utilizados por otros objetos en el lado emisor. Por ejemplo, Node.js marca los ArrayBuffer que utiliza para su Buffer pool con esto.

Esta operación no se puede deshacer.

js
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 generará 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 se ha 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)

Agregado en: v21.0.0

Verifica si un objeto está marcado como no transferible con markAsUntransferable().

js
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)

Agregado 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 genera un error. Esta es una operación sin efecto si object es un valor primitivo.

Esto no tiene efecto en ArrayBuffer ni en ningún objeto similar a Buffer.

Esta operación no se puede deshacer.

js
const { markAsUncloneable } = require('node:worker_threads')

const anyObject = { foo: 'bar' }
markAsUncloneable(anyObject)
const { port1 } = new MessageChannel()
try {
  // Esto generará 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

Transfiere un MessagePort a un contexto vm diferente. El objeto port original se vuelve inutilizable, y la instancia MessagePort devuelta toma su lugar.

El MessagePort devuelto es un objeto en el contexto de destino y hereda de su clase global Object. Los objetos pasados al listener 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 se puede usar port.onmessage() para recibir eventos usando este.

worker.parentPort

Añadido 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 usando parentPort.postMessage() están disponibles en el hilo padre usando worker.on('message'), y los mensajes enviados desde el hilo padre usando worker.postMessage() están disponibles en este hilo usando parentPort.on('message').

js
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, se envía de vuelta:
  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

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 mientras procesaba 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-hijo, use require('node:worker_threads').parentPort.postMessage() y worker.postMessage() para que los hilos se comuniquen.

El ejemplo siguiente muestra el uso de postMessageToThread: crea 10 hilos anidados, el último intentará comunicarse con el hilo principal.

js
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
js
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ónCambios
v15.12.0El argumento port ahora también puede referirse a un BroadcastChannel.
v12.3.0Añadido en: v12.3.0

Recibe un solo mensaje desde un MessagePort dado. Si no hay ningún mensaje disponible, se devuelve undefined, de lo contrario, se devuelve un objeto con una única propiedad message que contiene la carga útil del mensaje, correspondiente al mensaje más antiguo en la cola del MessagePort.

js
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 usa esta función, no se emite ningún evento 'message' y no se invoca al listener onmessage.

worker.resourceLimits

Añadido en: v13.2.0, v12.16.0

Proporciona el conjunto de restricciones de recursos del motor de JS dentro de este hilo Worker. Si la opción resourceLimits se pasó al constructor Worker, esto coincide con sus valores.

Si esto se usa en el hilo principal, su valor es un objeto vacío.

worker.SHARE_ENV

Añadido 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.

js
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ónCambios
v17.5.0, v16.15.0Ya no es experimental.
v15.12.0, v14.18.0Añadido en: v15.12.0, v14.18.0
  • key <any> Cualquier valor JavaScript arbitrario y clonable que se pueda usar como clave de <Map>.
  • value <any> Cualquier valor JavaScript arbitrario y clonable que se clonará y se pasará automáticamente a todas las nuevas instancias de Worker. Si value se pasa como undefined, se eliminará cualquier valor establecido previamente para la key.

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

Añadido en: v10.5.0

Un identificador entero para el hilo actual. En el objeto worker correspondiente (si existe), 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 de JavaScript arbitrario que contiene un clon de los datos pasados al constructor Worker de este hilo.

Los datos se clonan como si se utilizara postMessage(), según el algoritmo de clonación estructurada HTML.

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

if (isMainThread) {
  const worker = new Worker(__filename, { workerData: '¡Hola, mundo!' })
} else {
  console.log(workerData) // Imprime '¡Hola, mundo!'.
}

Clase: BroadcastChannel extends EventTarget

[Historial]

VersiónCambios
v18.0.0Ya no es experimental.
v15.4.0Agregado 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.

js
'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('hola desde cada worker')
  bc.close()
}

new BroadcastChannel(name)

Añadido 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 utilizando ${name}.

broadcastChannel.close()

Añadido en: v15.4.0

Cierra la conexión BroadcastChannel.

broadcastChannel.onmessage

Añadido en: v15.4.0

  • Tipo: <Function> Se invoca con un solo argumento MessageEvent cuando se recibe un mensaje.

broadcastChannel.onmessageerror

Añadido en: v15.4.0

  • Tipo: <Function> Se invoca cuando un mensaje recibido no se puede deserializar.

broadcastChannel.postMessage(message)

Añadido 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() no permite que el programa se cierre si es el único controlador activo restante (el comportamiento predeterminado). Si el puerto es ref()ed, llamar a ref() nuevamente no tiene 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() nuevamente no tiene efecto.

Clase: MessageChannel

Agregado en: v10.5.0

Las instancias de la clase worker.MessageChannel representan un canal de comunicaciones asíncrono y bidireccional. El MessageChannel no tiene métodos propios. new MessageChannel() produce un objeto con propiedades port1 y port2, que hacen referencia a instancias de MessagePort enlazadas.

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

const { port1, port2 } = new MessageChannel()
port1.on('message', message => console.log('recibido', message))
port2.postMessage({ foo: 'bar' })
// Imprime: recibido { foo: 'bar' } desde el listener `port1.on('message')`

Clase: MessagePort

[Historial]

VersiónCambios
v14.7.0Esta clase ahora hereda de EventTarget en lugar de EventEmitter.
v10.5.0Añadido en: v10.5.0

Las instancias de la clase worker.MessagePort representan un extremo de un canal de comunicaciones asíncrono y bidireccional. Se puede utilizar para transferir datos estructurados, regiones de memoria y otros MessagePorts entre diferentes Workers.

Esta implementación coincide con los MessagePort de los navegadores.

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.

js
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 en este evento reciben un clon del parámetro value tal como se pasó a postMessage() y ningún argumento adicional.

Evento: 'messageerror'

Añadido en: v14.5.0, v12.19.0

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 de la API de Node.js se reciben en un vm.Context (donde las API de Node.js no están disponibles actualmente).

port.close()

Agregado en: v10.5.0

Deshabilita el envío posterior de mensajes en ambos lados de la conexión. Este método se puede llamar cuando no se 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ónCambios
v21.0.0Se lanza un error cuando hay un objeto no transferible en la lista de transferencia.
v15.6.0Se agregó X509Certificate a la lista de tipos clonables.
v15.0.0Se agregó CryptoKey a la lista de tipos clonables.
v15.14.0, v14.18.0Se agregó 'BlockList' a la lista de tipos clonables.
v15.9.0, v14.18.0Se agregaron los tipos 'Histogram' a la lista de tipos clonables.
v14.5.0, v12.19.0Se agregó KeyObject a la lista de tipos clonables.
v14.5.0, v12.19.0Se agregó FileHandle a la lista de tipos transferibles.
v10.5.0Agregado en: v10.5.0

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:

js
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 se pueden usar en el lado emisor del canal (incluso si no están contenidos en value). A diferencia de con los procesos secundarios, actualmente no se admite la transferencia de identificadores como los sockets de red.

Si value contiene instancias de SharedArrayBuffer, se puede acceder a ellas desde cualquiera de los dos hilos. No se pueden incluir en transferList.

value aún puede contener instancias de ArrayBuffer que no están en transferList; en ese caso, la memoria subyacente se copia en lugar de moverse.

js
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])

// Se puede acceder a la memoria para el `sharedUint8Array` desde el
// original y la copia recibida por `.on('message')`:
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4))
port2.postMessage(sharedUint8Array)

// Esto transfiere un puerto de mensaje recién creado al receptor.
// Esto se puede utilizar, por ejemplo, para crear canales de comunicación entre
// varios hilos `Worker` que son hijos del mismo hilo principal.
const otherChannel = new MessageChannel()
port2.postMessage({ port: otherChannel.port1 }, [otherChannel.port1])

El objeto de mensaje se clona de inmediato 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 sin procesar, 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 utilizar una lista de transferencia para transferir un ArrayBuffer, ya que hacerlo provoca que todas las instancias de TypedArray y Buffer que comparten el mismo ArrayBuffer se vuelvan inutilizables.

js
const ab = new ArrayBuffer(10)

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

console.log(u2.length) // imprime 5

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

console.log(u2.length) // imprime 0

Para las instancias de Buffer, específicamente, si el ArrayBuffer subyacente se puede transferir o clonar depende totalmente de cómo se crearon las instancias, lo que a menudo no se puede determinar de manera confiable.

Un ArrayBuffer se puede marcar con markAsUntransferable() para indicar que siempre se debe clonar y nunca transferir.

Dependiendo de cómo se creó una instancia de Buffer, puede que sea propietaria o no de su ArrayBuffer subyacente. Un ArrayBuffer no se debe transferir a menos que se sepa que la instancia de Buffer es propietaria del mismo. En particular, para los Buffer creados a partir del pool interno de Buffer (utilizando, por ejemplo, Buffer.from() o Buffer.allocUnsafe()), no es posible transferirlos 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 utilizando Buffer.alloc() o Buffer.allocUnsafeSlow() siempre se pueden transferir, pero hacerlo hace que todas las demás vistas existentes de esos ArrayBuffer sean inutilizables.

Consideraciones al clonar objetos con prototipos, clases y descriptores de acceso

Debido a que la clonación de objetos utiliza el algoritmo de clonación estructurada HTML, las propiedades no enumerables, los descriptores de acceso de propiedades y los prototipos de objetos no se conservan. En particular, los objetos Buffer se leerán como Uint8Arrays simples en el lado receptor, y las instancias de clases JavaScript se clonarán como objetos JavaScript simples.

js
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())

// Imprime: { c: 3 }

Esta limitación se extiende a muchos objetos incorporados, como el objeto global URL:

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

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

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

// Imprime: { }

port.hasRef()

Agregado en: v18.1.0, v16.17.0

[Estable: 1 - Experimental]

Estable: 1 Estabilidad: 1 - Experimental

Si es verdadero, el objeto MessagePort mantendrá activo el bucle de eventos de Node.js.

port.ref()

Agregado en: v10.5.0

Opuesto a unref(). Llamar a ref() en un puerto previamente unref() no permite que el programa se cierre si es el único controlador activo que queda (el comportamiento predeterminado). Si el puerto está ref(), llamar a ref() nuevamente no tiene efecto.

Si los listeners se adjuntan o se eliminan usando .on('message'), el puerto se ref() y se unref() automáticamente dependiendo de si existen listeners para el evento.

port.start()

Agregado en: v10.5.0

Comienza a recibir mensajes en este MessagePort. Al usar 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 MessagePort Web. En Node.js, solo es útil para ignorar mensajes cuando no hay un listener de eventos presente. Node.js también diverge en su manejo de .onmessage. Al establecerlo, se llama automáticamente a .start(), pero al anularlo, los mensajes se ponen en cola hasta que se establece un nuevo controlador o se descarta el puerto.

port.unref()

Agregado en: v10.5.0

Llamar a unref() en un puerto permite que el hilo salga si este es el único controlador activo en el sistema de eventos. Si el puerto ya está en unref()ed, llamar a unref() nuevamente no tiene efecto.

Si los listeners se adjuntan o se eliminan utilizando .on('message'), el puerto se ref() y unref() automáticamente dependiendo de si existen listeners para el evento.

Clase: Worker

Agregado en: v10.5.0

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 este.

Las diferencias notables dentro de un entorno Worker son:

Es posible crear instancias de Worker dentro de otros Workers.

Al igual que los Web Workers y el módulo node:cluster, la comunicación bidireccional se puede lograr mediante el paso de mensajes entre hilos. Internamente, un Worker tiene un par incorporado de MessagePorts que ya están asociados entre sí cuando se crea el Worker. Si bien el objeto MessagePort del lado principal no se expone directamente, 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 cualquiera de los hilos y pasar uno de los MessagePorts 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 de hilos.

js
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('recibido:', value)
  })
} else {
  parentPort.once('message', value => {
    assert(value.hereIsYourPort instanceof MessagePort)
    value.hereIsYourPort.postMessage('el worker está enviando esto')
    value.hereIsYourPort.close()
  })
}

new Worker(filename[, options])

[Historial]

VersiónCambios
v19.8.0, v18.16.0Se añadió soporte para una opción name, que permite agregar un nombre al título del worker para depuración.
v14.9.0El parámetro filename puede ser un objeto URL WHATWG utilizando el protocolo data:.
v14.9.0La opción trackUnmanagedFds se estableció en true de forma predeterminada.
v14.6.0, v12.19.0Se introdujo la opción trackUnmanagedFds.
v13.13.0, v12.17.0Se introdujo la opción transferList.
v13.12.0, v12.17.0El parámetro filename puede ser un objeto URL WHATWG utilizando el protocolo file:.
v13.4.0, v12.16.0Se introdujo la opción argv.
v13.2.0, v12.16.0Se introdujo la opción resourceLimits.
v10.5.0Agregado 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 objeto URL WHATWG utilizando el protocolo file: o data:. Cuando se utiliza una URL data:, los datos se interpretan basándose en el tipo MIME utilizando el cargador de módulos ECMAScript. Si options.eval es true, 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 anexarían a process.argv en el worker. Esto es muy similar a workerData, pero los valores están disponibles en el process.argv global como si se pasaran como opciones de la CLI al script.
    • env <Object> Si se establece, especifica el valor inicial de process.env dentro del hilo Worker. Como valor especial, se puede utilizar worker.SHARE_ENV para especificar que el hilo padre y el hilo hijo deben compartir sus variables de entorno; en ese caso, los cambios en el objeto process.env de un hilo también afectan al otro hilo. Predeterminado: process.env.
    • eval <boolean> Si es true y el primer argumento es una string, interpreta el primer argumento del constructor como un script que se ejecuta una vez que el worker está en línea.
    • execArgv <string[]> Lista de opciones de la CLI de node pasadas al worker. Las opciones de V8 (como --max-old-space-size) y las opciones que afectan al proceso (como --title) no son compatibles. Si se establece, esto se proporciona como process.execArgv dentro del worker. De forma predeterminada, las opciones se heredan del hilo padre.
    • stdin <boolean> Si esto se establece en true, entonces worker.stdin proporciona un flujo grabable cuyo contenido aparece como process.stdin dentro del Worker. De forma predeterminada, no se proporcionan datos.
    • stdout <boolean> Si esto se establece en true, entonces worker.stdout no se canaliza automáticamente a process.stdout en el padre.
    • stderr <boolean> Si esto se establece en true, entonces worker.stderr no se canaliza automáticamente a process.stderr en el padre.
    • workerData <any> Cualquier valor de JavaScript que se clona y está disponible como require('node:worker_threads').workerData. La clonación se produce tal 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 contiene functions).
    • trackUnmanagedFds <boolean> Si esto se establece en true, entonces el Worker rastrea los descriptores de archivos sin administrar gestionados a través de fs.open() y fs.close(), y los cierra cuando el Worker sale, de forma similar a otros recursos como los sockets de red o los descriptores de archivos gestionados a través de la API FileHandle. Esta opción es automáticamente heredada por todos los Worker anidados. Predeterminado: true.
    • transferList <Object[]> Si uno o más objetos similares a MessagePort se pasan en workerData, se requiere una transferList para esos elementos o se lanza ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST. Consulte port.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 terminación de la instancia de Worker. Estos límites solo afectan al motor JS, y no a datos externos, incluyendo ningún ArrayBuffers. Incluso si estos límites están establecidos, el proceso aún puede abortar si encuentra una situación global de falta de memoria.
    • maxOldGenerationSizeMb <number> El tamaño máximo del heap principal en MB. Si se establece el argumento de la línea de comandos --max-old-space-size, este valor anula esta configuración.
    • maxYoungGenerationSizeMb <number> El tamaño máximo de un espacio de heap para objetos creados recientemente. Si se establece el argumento de la línea de comandos --max-semi-space-size, este valor anula esta configuración.
    • codeRangeSizeMb <number> El tamaño de un rango de memoria preasignado utilizado para código generado.
    • stackSizeMb <number> El tamaño máximo predeterminado de la pila para el hilo. Los valores pequeños pueden llevar a instancias de Worker inutilizables. Predeterminado: 4.
    • name <string> Un name opcional para anexar al título del worker con fines de depuración/identificación, haciendo que el título final sea [worker ${id}] ${name}. Predeterminado: ''.

Evento: 'error'

Agregado en: v10.5.0

El evento 'error' se emite si el hilo de trabajo lanza una excepción no detectada. En ese caso, el trabajador se termina.

Evento: 'exit'

Agregado en: v10.5.0

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'

Agregado 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 evento 'exit' en el objeto Worker.

Evento: 'messageerror'

Agregado en: v14.5.0, v12.19.0

El evento 'messageerror' se emite cuando la deserialización de un mensaje falla.

Evento: 'online'

Agregado 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ónCambios
v19.1.0Soporte para opciones para configurar la instantánea del montón.
v13.9.0, v12.17.0Agregado en: v13.9.0, v12.17.0
  • options <Objeto>

    • exposeInternals <booleano> Si es true, expone los internos en la instantánea del montón. Predeterminado: false.
    • exposeNumericValues <booleano> Si es true, expone los valores numéricos en campos artificiales. Predeterminado: false.
  • Retorna: <Promise> Una promesa para un Readable Stream que contiene una instantánea del montón de V8.

Devuelve un flujo legible para una instantánea de 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', 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 utilizar para consultar información de rendimiento desde una instancia de trabajador. Similar a perf_hooks.performance.

performance.eventLoopUtilization([utilization1[, utilization2]])

Agregado en: v15.1.0, v14.17.0, v12.22.0

  • utilization1 <Objeto> El resultado de una llamada anterior a eventLoopUtilization().
  • utilization2 <Objeto> El resultado de una llamada anterior a eventLoopUtilization() antes de utilization1.
  • Devuelve: <Objeto>

La misma llamada que perf_hooks eventLoopUtilization(), excepto que se devuelven los valores de la instancia del trabajador.

Una diferencia es que, a diferencia del hilo principal, el bootstrapping dentro de un trabajador se realiza dentro del bucle de eventos. Por lo tanto, la utilización del bucle de eventos está disponible de inmediato una vez que el script del trabajador comienza la ejecución.

Un tiempo idle que no aumenta no indica que el trabajador esté atascado en el bootstrap. Los siguientes ejemplos muestran cómo la vida útil completa del trabajador nunca acumula ningún tiempo idle, pero aún puede procesar mensajes.

js
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 trabajador está disponible solo después de que se emita 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

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 salga si es el único controlador activo restante (el comportamiento predeterminado). Si el worker está ref()ed, llamar a ref() nuevamente 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

Agregado en: v10.5.0

Este es un flujo legible que contiene datos escritos en process.stderr dentro del hilo de trabajo. Si no se pasó stderr: true 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 de trabajo como process.stdin.

worker.stdout

Agregado en: v10.5.0

Este es un flujo legible que contiene datos escritos en process.stdout dentro del hilo de trabajo. Si no se pasó stdout: true al constructor Worker, entonces los datos se canalizan al flujo process.stdout del hilo principal.

worker.terminate()

[Historial]

VersiónCambios
v12.5.0Esta función ahora devuelve una Promesa. Pasar una función de devolución de llamada está obsoleto, y era inútil hasta esta versión, ya que el Worker se terminaba realmente de forma síncrona. La terminación es ahora una operación totalmente asíncrona.
v10.5.0Añadido en: v10.5.0

Detiene toda la ejecución de JavaScript en el hilo de trabajo lo antes posible. Devuelve una Promesa para el código de salida que se cumple cuando se emite el evento 'exit'.

worker.threadId

Añadido en: v10.5.0

Un identificador entero para el hilo referenciado. Dentro del hilo de trabajo, está disponible como require('node:worker_threads').threadId. Este valor es único para cada instancia Worker dentro de un solo proceso.

worker.unref()

Agregado en: v10.5.0

Llamar a unref() en un worker permite que el hilo se cierre si este es el único manejador activo en el sistema de eventos. Si el worker ya está unref() llamado, llamar a unref() de nuevo no tiene ningún efecto.

Notas

Bloqueo síncrono de stdio

Los Workers utilizan el paso de mensajes a través de <MessagePort> para implementar 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.

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')
}
js
'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')
}

Lanzar hilos de trabajador desde scripts de precarga

Tenga cuidado al lanzar hilos de trabajador desde scripts de precarga (scripts cargados y ejecutados usando el indicador de línea de comandos -r). A menos que la opción execArgv se establezca explícitamente, los nuevos hilos de trabajador 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 trabajador, cada hilo generado generará otro hasta que la aplicación se bloquee.