Skip to content

Thread di lavoro

[Stabile: 2 - Stabile]

Stabile: 2 Stabilità: 2 - Stabile

Codice sorgente: lib/worker_threads.js

Il modulo node:worker_threads abilita l'uso di thread che eseguono JavaScript in parallelo. Per accedervi:

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

I worker (thread) sono utili per eseguire operazioni JavaScript ad alta intensità di CPU. Non aiutano molto con il lavoro ad alta intensità di I/O. Le operazioni di I/O asincrone integrate di Node.js sono più efficienti di quanto possano esserlo i worker.

A differenza di child_process o cluster, worker_threads possono condividere la memoria. Lo fanno trasferendo istanze di ArrayBuffer o condividendo istanze di 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 si è fermato con codice di uscita ${code}`));
      });
    });
  };
} else {
  const { parse } = require('some-js-parsing-library');
  const script = workerData;
  parentPort.postMessage(parse(script));
}

L'esempio precedente genera un thread Worker per ogni chiamata parseJSAsync(). In pratica, utilizza un pool di worker per questo tipo di attività. Altrimenti, il sovraccarico della creazione di worker probabilmente supererebbe i loro vantaggi.

Quando implementi un pool di worker, utilizza l'API AsyncResource per informare gli strumenti di diagnostica (ad es. per fornire stack trace asincroni) sulla correlazione tra le attività e i loro risultati. Consulta "Utilizzo di AsyncResource per un pool di thread Worker" nella documentazione di async_hooks per un esempio di implementazione.

I thread di lavoro ereditano le opzioni non specifiche del processo per impostazione predefinita. Fai riferimento a Opzioni del costruttore Worker per sapere come personalizzare le opzioni dei thread di lavoro, in particolare le opzioni argv e execArgv.

worker.getEnvironmentData(key)

[Cronologia]

VersioneModifiche
v17.5.0, v16.15.0Non è più sperimentale.
v15.12.0, v14.18.0Aggiunto in: v15.12.0, v14.18.0
  • key <any> Qualsiasi valore JavaScript arbitrario e clonabile che può essere utilizzato come chiave di <Map>.
  • Restituisce: <any>

All'interno di un thread worker, worker.getEnvironmentData() restituisce un clone dei dati passati a worker.setEnvironmentData() del thread di generazione. Ogni nuovo Worker riceve automaticamente la propria copia dei dati ambientali.

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'));  // Stampa 'World!'.
}

worker.isMainThread

Aggiunto in: v10.5.0

È true se questo codice non è in esecuzione all'interno di un thread Worker.

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

if (isMainThread) {
  // Questo ricarica il file corrente all'interno di un'istanza Worker.
  new Worker(__filename);
} else {
  console.log('Inside Worker!');
  console.log(isMainThread);  // Stampa 'false'.
}

worker.markAsUntransferable(object)

Aggiunto in: v14.5.0, v12.19.0

  • object <any> Qualsiasi valore JavaScript arbitrario.

Contrassegna un oggetto come non trasferibile. Se object si verifica nell'elenco di trasferimento di una chiamata port.postMessage(), viene generato un errore. Questa è una no-op se object è un valore primitivo.

In particolare, questo ha senso per gli oggetti che possono essere clonati, piuttosto che trasferiti, e che sono utilizzati da altri oggetti sul lato mittente. Ad esempio, Node.js contrassegna gli ArrayBuffer che utilizza per il suo Buffer pool con questo.

Questa operazione non può essere annullata.

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 {
  // Questo genererà un errore, perché pooledBuffer non è trasferibile.
  port1.postMessage(typedArray1, [ typedArray1.buffer ]);
} catch (error) {
  // error.name === 'DataCloneError'
}

// La seguente riga stampa il contenuto di typedArray1 -- possiede ancora
// la sua memoria e non è stato trasferito. Senza
// `markAsUntransferable()`, questo stamperebbe un Uint8Array vuoto e la
// chiamata postMessage sarebbe andata a buon fine.
// Anche typedArray2 è intatto.
console.log(typedArray1);
console.log(typedArray2);

Non esiste un equivalente di questa API nei browser.

worker.isMarkedAsUntransferable(object)

Aggiunto in: v21.0.0

Verifica se un oggetto è contrassegnato come non trasferibile con markAsUntransferable().

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

const pooledBuffer = new ArrayBuffer(8);
markAsUntransferable(pooledBuffer);

isMarkedAsUntransferable(pooledBuffer);  // Restituisce true.

Non esiste un equivalente a questa API nei browser.

worker.markAsUncloneable(object)

Aggiunto in: v23.0.0

  • object <any> Qualsiasi valore JavaScript arbitrario.

Contrassegna un oggetto come non clonabile. Se object viene utilizzato come message in una chiamata a port.postMessage(), viene generato un errore. Questa è un'operazione no-op se object è un valore primitivo.

Questo non ha alcun effetto su ArrayBuffer o su qualsiasi oggetto simile a Buffer.

Questa operazione non può essere annullata.

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

const anyObject = { foo: 'bar' };
markAsUncloneable(anyObject);
const { port1 } = new MessageChannel();
try {
  // Questo genererà un errore, perché anyObject non è clonabile.
  port1.postMessage(anyObject);
} catch (error) {
  // error.name === 'DataCloneError'
}

Non esiste un equivalente a questa API nei browser.

worker.moveMessagePortToContext(port, contextifiedSandbox)

Aggiunto in: v11.13.0

Trasferisce una MessagePort a un vm Context diverso. L'oggetto port originale viene reso inutilizzabile e l'istanza MessagePort restituita prende il suo posto.

La MessagePort restituita è un oggetto nel contesto di destinazione ed eredita dalla sua classe globale Object. Gli oggetti passati al listener port.onmessage() vengono anch'essi creati nel contesto di destinazione ed ereditano dalla sua classe globale Object.

Tuttavia, la MessagePort creata non eredita più da EventTarget e solo port.onmessage() può essere utilizzata per ricevere eventi utilizzando essa.

worker.parentPort

Aggiunto in: v10.5.0

Se questo thread è un Worker, questo è un MessagePort che consente la comunicazione con il thread padre. I messaggi inviati usando parentPort.postMessage() sono disponibili nel thread padre usando worker.on('message') e i messaggi inviati dal thread padre usando worker.postMessage() sono disponibili in questo thread 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);  // Stampa 'Hello, world!'.
  });
  worker.postMessage('Hello, world!');
} else {
  // Quando viene ricevuto un messaggio dal thread padre, lo rimanda indietro:
  parentPort.once('message', (message) => {
    parentPort.postMessage(message);
  });
}

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

Aggiunto in: v22.5.0

[Stabile: 1 - Sperimentale]

Stabile: 1 Stabilità: 1.1 - Sviluppo attivo

Invia un valore a un altro worker, identificato dal suo ID thread.

Se il thread di destinazione non ha alcun listener per l'evento workerMessage, l'operazione genererà un errore ERR_WORKER_MESSAGING_FAILED.

Se il thread di destinazione ha generato un errore durante l'elaborazione dell'evento workerMessage, l'operazione genererà un errore ERR_WORKER_MESSAGING_ERRORED.

Questo metodo deve essere utilizzato quando il thread di destinazione non è il genitore o il figlio diretto del thread corrente. Se i due thread sono padre-figlio, utilizzare require('node:worker_threads').parentPort.postMessage() e worker.postMessage() per consentire ai thread di comunicare.

L'esempio seguente mostra l'uso di postMessageToThread: crea 10 thread nidificati, l'ultimo dei quali tenterà di comunicare con il thread principale.

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)

[Cronologia]

VersioneModifiche
v15.12.0L'argomento port può anche fare riferimento ora a un BroadcastChannel.
v12.3.0Aggiunto in: v12.3.0

Riceve un singolo messaggio da una data MessagePort. Se nessun messaggio è disponibile, viene restituito undefined, altrimenti un oggetto con una singola proprietà message che contiene il payload del messaggio, corrispondente al messaggio più vecchio nella coda della MessagePort.

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

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

Quando viene utilizzata questa funzione, nessun evento 'message' viene emesso e il listener onmessage non viene invocato.

worker.resourceLimits

Aggiunto in: v13.2.0, v12.16.0

Fornisce l'insieme di vincoli delle risorse del motore JS all'interno di questo thread Worker. Se l'opzione resourceLimits è stata passata al costruttore Worker, questo corrisponde ai suoi valori.

Se questo viene utilizzato nel thread principale, il suo valore è un oggetto vuoto.

worker.SHARE_ENV

Aggiunto in: v11.14.0

Un valore speciale che può essere passato come opzione env del costruttore Worker, per indicare che il thread corrente e il thread Worker devono condividere l'accesso in lettura e scrittura allo stesso insieme di variabili d'ambiente.

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);  // Stampa 'foo'.
  });

worker.setEnvironmentData(key[, value])

[Cronologia]

VersioneModifiche
v17.5.0, v16.15.0Non più sperimentale.
v15.12.0, v14.18.0Aggiunto in: v15.12.0, v14.18.0
  • key <any> Qualsiasi valore JavaScript arbitrario e clonabile che può essere utilizzato come chiave <Map>.
  • value <any> Qualsiasi valore JavaScript arbitrario e clonabile che verrà clonato e passato automaticamente a tutte le nuove istanze Worker. Se value viene passato come undefined, qualsiasi valore impostato precedentemente per la key verrà eliminato.

L'API worker.setEnvironmentData() imposta il contenuto di worker.getEnvironmentData() nel thread corrente e in tutte le nuove istanze Worker generate dal contesto corrente.

worker.threadId

Aggiunto in: v10.5.0

Un identificatore intero per il thread corrente. Sull'oggetto worker corrispondente (se presente), è disponibile come worker.threadId. Questo valore è univoco per ciascuna istanza Worker all'interno di un singolo processo.

worker.workerData

Aggiunto in: v10.5.0

Un valore JavaScript arbitrario che contiene un clone dei dati passati al costruttore Worker di questo thread.

I dati vengono clonati come se si utilizzasse postMessage(), secondo l'algoritmo di clonazione strutturata HTML.

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

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

Classe: BroadcastChannel extends EventTarget

[Cronologia]

VersioneModifiche
v18.0.0Non più sperimentale.
v15.4.0Aggiunto in: v15.4.0

Le istanze di BroadcastChannel consentono la comunicazione asincrona uno-a-molti con tutte le altre istanze di BroadcastChannel associate allo stesso nome di canale.

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('hello from every worker');
  bc.close();
}

new BroadcastChannel(name)

Aggiunto in: v15.4.0

  • name <any> Il nome del canale a cui connettersi. È consentito qualsiasi valore JavaScript che può essere convertito in una stringa usando ${name}.

broadcastChannel.close()

Aggiunto in: v15.4.0

Chiude la connessione BroadcastChannel.

broadcastChannel.onmessage

Aggiunto in: v15.4.0

  • Tipo: <Function> Invocato con un singolo argomento MessageEvent quando viene ricevuto un messaggio.

broadcastChannel.onmessageerror

Aggiunto in: v15.4.0

  • Tipo: <Funzione> Invocata quando un messaggio ricevuto non può essere deserializzato.

broadcastChannel.postMessage(message)

Aggiunto in: v15.4.0

  • message <any> Qualsiasi valore JavaScript clonabile.

broadcastChannel.ref()

Aggiunto in: v15.4.0

Opposto di unref(). Chiamare ref() su un BroadcastChannel precedentemente unref()ed non permette al programma di terminare se è l'unico handle attivo rimasto (il comportamento predefinito). Se la porta è ref()ed, chiamare ref() di nuovo non ha alcun effetto.

broadcastChannel.unref()

Aggiunto in: v15.4.0

Chiamare unref() su un BroadcastChannel permette al thread di terminare se questo è l'unico handle attivo nel sistema di eventi. Se il BroadcastChannel è già unref()ed, chiamare unref() di nuovo non ha alcun effetto.

Classe: MessageChannel

Aggiunto in: v10.5.0

Le istanze della classe worker.MessageChannel rappresentano un canale di comunicazione asincrono a due vie. MessageChannel non ha metodi propri. new MessageChannel() restituisce un oggetto con proprietà port1 e port2, che si riferiscono a istanze MessagePort collegate.

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

const { port1, port2 } = new MessageChannel();
port1.on('message', (message) => console.log('ricevuto', message));
port2.postMessage({ foo: 'bar' });
// Stampa: ricevuto { foo: 'bar' } dall'ascoltatore `port1.on('message')`

Classe: MessagePort

[Cronologia]

VersioneModifiche
v14.7.0Questa classe ora eredita da EventTarget anziché da EventEmitter.
v10.5.0Aggiunto in: v10.5.0

Le istanze della classe worker.MessagePort rappresentano un'estremità di un canale di comunicazione asincrono a due vie. Può essere utilizzato per trasferire dati strutturati, regioni di memoria e altri MessagePort tra diversi Workers.

Questa implementazione corrisponde a MessagePort del browser.

Evento: 'close'

Aggiunto in: v10.5.0

L'evento 'close' viene emesso quando uno dei due lati del canale è stato disconnesso.

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

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

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

Evento: 'message'

Aggiunto in: v10.5.0

  • value <any> Il valore trasmesso

L'evento 'message' viene emesso per ogni messaggio in arrivo, contenente l'input clonato di port.postMessage().

I listener su questo evento ricevono un clone del parametro value come passato a postMessage() e nessun altro argomento.

Evento: 'messageerror'

Aggiunto in: v14.5.0, v12.19.0

L'evento 'messageerror' viene emesso quando la deserializzazione di un messaggio non è riuscita.

Attualmente, questo evento viene emesso quando si verifica un errore durante la creazione di un'istanza dell'oggetto JS inviato all'estremità ricevente. Tali situazioni sono rare, ma possono verificarsi, ad esempio, quando determinati oggetti API di Node.js vengono ricevuti in un vm.Context (dove le API di Node.js non sono attualmente disponibili).

port.close()

Aggiunto in: v10.5.0

Disabilita l'ulteriore invio di messaggi su entrambi i lati della connessione. Questo metodo può essere chiamato quando non si verificherà un'ulteriore comunicazione su questa MessagePort.

L'evento 'close' viene emesso su entrambe le istanze MessagePort che fanno parte del canale.

port.postMessage(value[, transferList])

[Cronologia]

VersioneModifiche
v21.0.0Viene generato un errore quando un oggetto non trasferibile si trova nell'elenco di trasferimento.
v15.6.0Aggiunto X509Certificate all'elenco dei tipi clonabili.
v15.0.0Aggiunto CryptoKey all'elenco dei tipi clonabili.
v15.14.0, v14.18.0Aggiunto 'BlockList' all'elenco dei tipi clonabili.
v15.9.0, v14.18.0Aggiunto tipi 'Histogram' all'elenco dei tipi clonabili.
v14.5.0, v12.19.0Aggiunto KeyObject all'elenco dei tipi clonabili.
v14.5.0, v12.19.0Aggiunto FileHandle all'elenco dei tipi trasferibili.
v10.5.0Aggiunto in: v10.5.0

Invia un valore JavaScript al lato ricevente di questo canale. value viene trasferito in un modo compatibile con l'algoritmo di clonazione strutturata HTML.

In particolare, le differenze significative rispetto a JSON sono:

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

transferList può essere un elenco di oggetti ArrayBuffer, MessagePort e FileHandle. Dopo il trasferimento, non sono più utilizzabili sul lato mittente del canale (anche se non sono contenuti in value). A differenza dei processi figlio, il trasferimento di handle come i socket di rete non è attualmente supportato.

Se value contiene istanze di SharedArrayBuffer, queste sono accessibili da entrambi i thread. Non possono essere elencati in transferList.

value può ancora contenere istanze di ArrayBuffer che non sono in transferList; in tal caso, la memoria sottostante viene copiata anziché spostata.

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 ]);
// Questo invia una copia di `uint8Array`:
port2.postMessage(uint8Array);
// Questo non copia i dati, ma rende `uint8Array` inutilizzabile:
port2.postMessage(uint8Array, [ uint8Array.buffer ]);

// La memoria per `sharedUint8Array` è accessibile sia dall'originale
// che dalla copia ricevuta da `.on('message')`:
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4));
port2.postMessage(sharedUint8Array);

// Questo trasferisce una message port appena creata al ricevitore.
// Questo può essere utilizzato, ad esempio, per creare canali di comunicazione tra
// più thread `Worker` che sono figli dello stesso thread padre.
const otherChannel = new MessageChannel();
port2.postMessage({ port: otherChannel.port1 }, [ otherChannel.port1 ]);

L'oggetto messaggio viene clonato immediatamente e può essere modificato dopo la pubblicazione senza avere effetti collaterali.

Per ulteriori informazioni sui meccanismi di serializzazione e deserializzazione alla base di questa API, consulta l'API di serializzazione del modulo node:v8.

Considerazioni durante il trasferimento di TypedArray e Buffer

Tutte le istanze TypedArray e Buffer sono viste su un ArrayBuffer sottostante. Cioè, è l'ArrayBuffer che effettivamente memorizza i dati grezzi mentre gli oggetti TypedArray e Buffer forniscono un modo per visualizzare e manipolare i dati. È possibile e comune creare più viste sulla stessa istanza ArrayBuffer. È necessario prestare molta attenzione quando si utilizza un elenco di trasferimento per trasferire un ArrayBuffer poiché ciò rende inutilizzabili tutte le istanze TypedArray e Buffer che condividono lo stesso ArrayBuffer.

js
const ab = new ArrayBuffer(10);

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

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

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

console.log(u2.length);  // stampa 0

Per le istanze Buffer, in particolare, se l'ArrayBuffer sottostante può essere trasferito o clonato dipende interamente da come sono state create le istanze, il che spesso non può essere determinato in modo affidabile.

Un ArrayBuffer può essere contrassegnato con markAsUntransferable() per indicare che dovrebbe sempre essere clonato e mai trasferito.

A seconda di come è stata creata un'istanza Buffer, potrebbe o meno possedere il suo ArrayBuffer sottostante. Un ArrayBuffer non deve essere trasferito a meno che non sia noto che l'istanza Buffer lo possiede. In particolare, per i Buffer creati dal pool Buffer interno (utilizzando, ad esempio, Buffer.from() o Buffer.allocUnsafe()), il loro trasferimento non è possibile e vengono sempre clonati, il che invia una copia dell'intero pool Buffer. Questo comportamento può comportare un utilizzo di memoria più elevato non intenzionale e possibili problemi di sicurezza.

Vedere Buffer.allocUnsafe() per maggiori dettagli sul pooling di Buffer.

Gli ArrayBuffer per le istanze Buffer create utilizzando Buffer.alloc() o Buffer.allocUnsafeSlow() possono sempre essere trasferiti, ma ciò rende inutilizzabili tutte le altre viste esistenti di tali ArrayBuffer.

Considerazioni durante la clonazione di oggetti con prototipi, classi e accessorie

Poiché la clonazione di oggetti utilizza l'algoritmo di clonazione strutturata HTML, le proprietà non enumerabili, le accessorie di proprietà e i prototipi di oggetti non vengono preservati. In particolare, gli oggetti Buffer verranno letti come semplici Uint8Array sul lato ricevente e le istanze di classi JavaScript verranno clonate come semplici oggetti JavaScript.

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

// Prints: { c: 3 }

Questa limitazione si estende a molti oggetti integrati, come l'oggetto globale URL:

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

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

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

// Prints: { }

port.hasRef()

Aggiunto in: v18.1.0, v16.17.0

[Stabile: 1 - Sperimentale]

Stabile: 1 Stabilità: 1 - Sperimentale

Se vero, l'oggetto MessagePort manterrà attivo il ciclo di eventi di Node.js.

port.ref()

Aggiunto in: v10.5.0

Opposto di unref(). Chiamare ref() su una porta precedentemente unref()ed non consente al programma di uscire se è l'unico handle attivo rimasto (il comportamento predefinito). Se la porta è ref()ed, chiamare di nuovo ref() non ha alcun effetto.

Se i listener vengono allegati o rimossi utilizzando .on('message'), la porta viene ref()ed e unref()ed automaticamente a seconda che esistano listener per l'evento.

port.start()

Aggiunto in: v10.5.0

Inizia a ricevere messaggi su questa MessagePort. Quando si utilizza questa porta come un emettitore di eventi, questo metodo viene chiamato automaticamente una volta che i listener 'message' sono collegati.

Questo metodo esiste per parità con l'API Web MessagePort. In Node.js, è utile solo per ignorare i messaggi quando non è presente alcun listener di eventi. Node.js diverge anche nella sua gestione di .onmessage. Impostandolo chiama automaticamente .start(), ma disattivandolo permette ai messaggi di mettersi in coda fino a quando non viene impostato un nuovo gestore o la porta viene scartata.

port.unref()

Aggiunto in: v10.5.0

Chiamare unref() su una porta permette al thread di terminare se questo è l'unico handle attivo nel sistema di eventi. Se la porta è già unref()ed chiamare unref() di nuovo non ha alcun effetto.

Se i listener sono collegati o rimossi usando .on('message'), la porta è ref()ed e unref()ed automaticamente a seconda che i listener per l'evento esistano.

Classe: Worker

Aggiunto in: v10.5.0

La classe Worker rappresenta un thread di esecuzione JavaScript indipendente. La maggior parte delle API di Node.js sono disponibili al suo interno.

Le differenze notevoli all'interno di un ambiente Worker sono:

È possibile creare istanze Worker all'interno di altri Worker.

Come i Web Worker e il modulo node:cluster, la comunicazione bidirezionale può essere ottenuta tramite il passaggio di messaggi tra thread. Internamente, un Worker ha una coppia incorporata di MessagePort che sono già associate l'una all'altra quando viene creato il Worker. Mentre l'oggetto MessagePort sul lato padre non è direttamente esposto, le sue funzionalità sono esposte tramite worker.postMessage() e l'evento worker.on('message') sull'oggetto Worker per il thread padre.

Per creare canali di messaggistica personalizzati (che è incoraggiato rispetto all'utilizzo del canale globale predefinito perché facilita la separazione delle preoccupazioni), gli utenti possono creare un oggetto MessageChannel su uno dei due thread e passare una delle MessagePort su quel MessageChannel all'altro thread attraverso un canale preesistente, come quello globale.

Vedere port.postMessage() per maggiori informazioni su come i messaggi vengono passati e che tipo di valori JavaScript possono essere trasportati con successo attraverso la barriera del thread.

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('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])

[Cronologia]

VersioneModifiche
v19.8.0, v18.16.0Aggiunto il supporto per un'opzione name, che consente di aggiungere un nome al titolo del worker per il debug.
v14.9.0Il parametro filename può essere un oggetto URL WHATWG che utilizza il protocollo data:.
v14.9.0L'opzione trackUnmanagedFds è stata impostata su true per impostazione predefinita.
v14.6.0, v12.19.0È stata introdotta l'opzione trackUnmanagedFds.
v13.13.0, v12.17.0È stata introdotta l'opzione transferList.
v13.12.0, v12.17.0Il parametro filename può essere un oggetto URL WHATWG che utilizza il protocollo file:.
v13.4.0, v12.16.0È stata introdotta l'opzione argv.
v13.2.0, v12.16.0È stata introdotta l'opzione resourceLimits.
v10.5.0Aggiunto in: v10.5.0
  • filename <stringa> | <URL> Il percorso dello script principale o del modulo del Worker. Deve essere un percorso assoluto o un percorso relativo (ovvero, relativo alla directory di lavoro corrente) che inizia con ./ o ../, oppure un oggetto URL WHATWG che utilizza il protocollo file: o data:. Quando si utilizza un data: URL, i dati vengono interpretati in base al tipo MIME utilizzando il caricatore di moduli ECMAScript. Se options.eval è true, questo è una stringa contenente codice JavaScript anziché un percorso.
  • options <Oggetto>
    • argv <any[]> Elenco di argomenti che verrebbero convertiti in stringa e aggiunti a process.argv nel worker. Questo è per lo più simile a workerData, ma i valori sono disponibili su process.argv globale come se fossero stati passati come opzioni CLI allo script.

    • env <Oggetto> Se impostato, specifica il valore iniziale di process.env all'interno del thread Worker. Come valore speciale, è possibile utilizzare worker.SHARE_ENV per specificare che il thread padre e il thread figlio devono condividere le loro variabili di ambiente; in tal caso, le modifiche all'oggetto process.env di un thread influiscono anche sull'altro thread. Predefinito: process.env.

    • eval <boolean> Se true e il primo argomento è una stringa, interpreta il primo argomento del costruttore come uno script che viene eseguito una volta che il worker è online.

    • execArgv <string[]> Elenco delle opzioni CLI di node passate al worker. Le opzioni V8 (come --max-old-space-size) e le opzioni che influiscono sul processo (come --title) non sono supportate. Se impostato, questo viene fornito come process.execArgv all'interno del worker. Per impostazione predefinita, le opzioni vengono ereditate dal thread padre.

    • stdin <boolean> Se questo è impostato su true, allora worker.stdin fornisce un flusso scrivibile il cui contenuto appare come process.stdin all'interno del Worker. Per impostazione predefinita, non vengono forniti dati.

    • stdout <boolean> Se questo è impostato su true, allora worker.stdout non viene automaticamente inviato tramite pipe a process.stdout nel padre.

    • stderr <boolean> Se questo è impostato su true, allora worker.stderr non viene automaticamente inviato tramite pipe a process.stderr nel padre.

    • workerData <any> Qualsiasi valore JavaScript che viene clonato e reso disponibile come require('node:worker_threads').workerData. La clonazione avviene come descritto nell'algoritmo di clonazione strutturata HTML, e viene generato un errore se l'oggetto non può essere clonato (ad esempio, perché contiene function).

    • trackUnmanagedFds <boolean> Se questo è impostato su true, allora il Worker tiene traccia dei descrittori di file raw gestiti tramite fs.open() e fs.close(), e li chiude quando il Worker esce, in modo simile ad altre risorse come i socket di rete o i descrittori di file gestiti tramite l'API FileHandle. Questa opzione viene ereditata automaticamente da tutti i Worker nidificati. Predefinito: true.

    • transferList <Object[]> Se uno o più oggetti simili a MessagePort vengono passati in workerData, è richiesto un transferList per quegli elementi oppure viene generato ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST. Vedi port.postMessage() per maggiori informazioni.

    • resourceLimits <Oggetto> Un insieme opzionale di limiti di risorse per la nuova istanza del motore JS. Il raggiungimento di questi limiti porta alla terminazione dell'istanza Worker. Questi limiti influiscono solo sul motore JS e su nessun dato esterno, inclusi gli ArrayBuffer. Anche se questi limiti sono impostati, il processo potrebbe comunque interrompersi se si verifica una situazione globale di esaurimento della memoria.

    • maxOldGenerationSizeMb <numero> La dimensione massima dell'heap principale in MB. Se l'argomento della riga di comando --max-old-space-size è impostato, sovrascrive questa impostazione.

    • maxYoungGenerationSizeMb <numero> La dimensione massima di uno spazio heap per oggetti creati di recente. Se l'argomento della riga di comando --max-semi-space-size è impostato, sovrascrive questa impostazione.

    • codeRangeSizeMb <numero> La dimensione di un intervallo di memoria pre-allocato utilizzato per il codice generato.

    • stackSizeMb <numero> La dimensione massima predefinita dello stack per il thread. Valori piccoli possono portare a istanze Worker inutilizzabili. Predefinito: 4.

    • name <stringa> Un name opzionale da aggiungere al titolo del worker per scopi di debug/identificazione, rendendo il titolo finale come [worker ${id}] ${name}. Predefinito: ''.

Evento: 'error'

Aggiunto in: v10.5.0

L'evento 'error' viene emesso se il thread worker genera un'eccezione non catturata. In tal caso, il worker viene terminato.

Evento: 'exit'

Aggiunto in: v10.5.0

L'evento 'exit' viene emesso una volta che il worker si è fermato. Se il worker è uscito chiamando process.exit(), il parametro exitCode è il codice di uscita passato. Se il worker è stato terminato, il parametro exitCode è 1.

Questo è l'evento finale emesso da qualsiasi istanza Worker.

Evento: 'message'

Aggiunto in: v10.5.0

  • value <any> Il valore trasmesso

L'evento 'message' viene emesso quando il thread worker ha invocato require('node:worker_threads').parentPort.postMessage(). Vedi l'evento port.on('message') per maggiori dettagli.

Tutti i messaggi inviati dal thread worker vengono emessi prima che l''exit' event venga emesso sull'oggetto Worker.

Evento: 'messageerror'

Aggiunto in: v14.5.0, v12.19.0

L'evento 'messageerror' viene emesso quando la deserializzazione di un messaggio non è andata a buon fine.

Evento: 'online'

Aggiunto in: v10.5.0

L'evento 'online' viene emesso quando il thread worker ha iniziato a eseguire codice JavaScript.

worker.getHeapSnapshot([options])

[Cronologia]

VersioneModifiche
v19.1.0Supporto delle opzioni per configurare lo snapshot dell'heap.
v13.9.0, v12.17.0Aggiunto in: v13.9.0, v12.17.0
  • options <Object>

    • exposeInternals <boolean> Se vero, espone gli interni nello snapshot dell'heap. Predefinito: false.
    • exposeNumericValues <boolean> Se vero, espone i valori numerici in campi artificiali. Predefinito: false.
  • Restituisce: <Promise> Una promise per uno Stream leggibile contenente uno snapshot dell'heap V8

Restituisce uno stream leggibile per uno snapshot V8 dello stato corrente del Worker. Vedere v8.getHeapSnapshot() per maggiori dettagli.

Se il thread Worker non è più in esecuzione, il che può verificarsi prima che l'evento 'exit' event venga emesso, la Promise restituita viene rifiutata immediatamente con un errore ERR_WORKER_NOT_RUNNING.

worker.performance

Aggiunto in: v15.1.0, v14.17.0, v12.22.0

Un oggetto che può essere utilizzato per interrogare le informazioni sulle prestazioni da un'istanza di worker. Simile a perf_hooks.performance.

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

Aggiunto in: v15.1.0, v14.17.0, v12.22.0

  • utilization1 <Object> Il risultato di una precedente chiamata a eventLoopUtilization().
  • utilization2 <Object> Il risultato di una precedente chiamata a eventLoopUtilization() prima di utilization1.
  • Restituisce: <Object>

La stessa chiamata di perf_hooks eventLoopUtilization(), tranne per il fatto che vengono restituiti i valori dell'istanza del worker.

Una differenza è che, a differenza del thread principale, il bootstrapping all'interno di un worker viene eseguito all'interno del ciclo di eventi. Quindi l'utilizzo del ciclo di eventi è immediatamente disponibile una volta che lo script del worker inizia l'esecuzione.

Un tempo idle che non aumenta non indica che il worker è bloccato nel bootstrap. I seguenti esempi mostrano come l'intera durata del worker non accumuli mai alcun tempo idle, ma sia comunque in grado di elaborare i messaggi.

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

L'utilizzo del ciclo di eventi di un worker è disponibile solo dopo l'emissione dell'evento 'online' event e, se chiamato prima di questo, o dopo l'evento 'exit' event, tutte le proprietà hanno il valore di 0.

worker.postMessage(value[, transferList])

Aggiunto in: v10.5.0

Invia un messaggio al worker che viene ricevuto tramite require('node:worker_threads').parentPort.on('message'). Vedi port.postMessage() per maggiori dettagli.

worker.ref()

Aggiunto in: v10.5.0

L'opposto di unref(), chiamare ref() su un worker precedentemente unref()ed non permette al programma di uscire se è l'unico handle attivo rimasto (il comportamento predefinito). Se il worker è ref()ed, chiamare ref() di nuovo non ha alcun effetto.

worker.resourceLimits

Aggiunto in: v13.2.0, v12.16.0

Fornisce l'insieme dei vincoli delle risorse del motore JS per questo thread Worker. Se l'opzione resourceLimits è stata passata al costruttore Worker, questo corrisponde ai suoi valori.

Se il worker si è fermato, il valore di ritorno è un oggetto vuoto.

worker.stderr

Aggiunto in: v10.5.0

Questo è un flusso leggibile che contiene i dati scritti in process.stderr all'interno del thread worker. Se stderr: true non è stato passato al costruttore Worker, allora i dati vengono indirizzati al flusso process.stderr del thread principale.

worker.stdin

Aggiunto in: v10.5.0

Se è stato passato stdin: true al costruttore Worker, questo è un flusso scrivibile. I dati scritti in questo flusso saranno resi disponibili nel thread worker come process.stdin.

worker.stdout

Aggiunto in: v10.5.0

Questo è un flusso leggibile che contiene i dati scritti in process.stdout all'interno del thread worker. Se stdout: true non è stato passato al costruttore Worker, allora i dati vengono indirizzati al flusso process.stdout del thread padre.

worker.terminate()

[Cronologia]

VersioneModifiche
v12.5.0Questa funzione ora restituisce una Promise. Passare un callback è deprecato ed è stato inutile fino a questa versione, poiché il Worker veniva effettivamente terminato in modo sincrono. La terminazione è ora un'operazione completamente asincrona.
v10.5.0Aggiunto in: v10.5.0

Interrompe l'esecuzione di tutti i JavaScript nel thread worker il prima possibile. Restituisce una Promise per il codice di uscita che viene soddisfatta quando viene emesso l' 'exit' event.

worker.threadId

Aggiunto in: v10.5.0

Un identificatore intero per il thread referenziato. All'interno del thread worker, è disponibile come require('node:worker_threads').threadId. Questo valore è univoco per ogni istanza Worker all'interno di un singolo processo.

worker.unref()

Aggiunto in: v10.5.0

Chiamare unref() su un worker consente al thread di uscire se questo è l'unico handle attivo nel sistema di eventi. Se il worker è già unref()ed chiamare unref() di nuovo non ha effetto.

Note

Blocco sincrono di stdio

I Worker utilizzano il passaggio di messaggi tramite <MessagePort> per implementare le interazioni con stdio. Ciò significa che l'output stdio proveniente da un Worker può essere bloccato da codice sincrono all'estremità ricevente che sta bloccando l'event loop di 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++) {
    // Ciclo per simulare il lavoro.
  }
} else {
  // Questo output sarà bloccato dal ciclo for nel thread principale.
  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++) {
    // Ciclo per simulare il lavoro.
  }
} else {
  // Questo output sarà bloccato dal ciclo for nel thread principale.
  console.log('foo');
}

Avvio di thread worker da script di precaricamento

Prestare attenzione quando si avviano thread worker da script di precaricamento (script caricati ed eseguiti utilizzando il flag della riga di comando -r). A meno che l'opzione execArgv non sia impostata esplicitamente, i nuovi thread Worker ereditano automaticamente i flag della riga di comando dal processo in esecuzione e precaricheranno gli stessi script di precaricamento del thread principale. Se lo script di precaricamento avvia incondizionatamente un thread worker, ogni thread generato ne genererà un altro fino a quando l'applicazione non si arresta in modo anomalo.