Skip to content

Threads de travail (Worker threads)

[Stable: 2 - Stable]

Stable: 2 Stabilité: 2 - Stable

Code source: lib/worker_threads.js

Le module node:worker_threads permet d'utiliser des threads qui exécutent JavaScript en parallèle. Pour y accéder :

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

Les Workers (threads) sont utiles pour effectuer des opérations JavaScript gourmandes en CPU. Ils ne sont pas très utiles pour les tâches gourmandes en E/S. Les opérations d'E/S asynchrones intégrées de Node.js sont plus efficaces que les Workers.

Contrairement à child_process ou cluster, worker_threads peuvent partager de la mémoire. Ils le font en transférant des instances ArrayBuffer ou en partageant des instances 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 stopped with exit code ${code}`));
      });
    });
  };
} else {
  const { parse } = require('some-js-parsing-library');
  const script = workerData;
  parentPort.postMessage(parse(script));
}

L'exemple ci-dessus génère un thread Worker pour chaque appel parseJSAsync(). En pratique, utilisez un pool de Workers pour ce type de tâches. Sinon, les frais généraux liés à la création de Workers dépasseraient probablement leurs avantages.

Lors de l'implémentation d'un pool de workers, utilisez l'API AsyncResource pour informer les outils de diagnostic (par exemple, pour fournir des traces de pile asynchrones) de la corrélation entre les tâches et leurs résultats. Consultez la section "Utilisation de AsyncResource pour un pool de threads Worker" dans la documentation async_hooks pour un exemple d'implémentation.

Les threads de travail héritent des options non spécifiques au processus par défaut. Reportez-vous à Options du constructeur Worker pour savoir comment personnaliser les options des threads de travail, en particulier les options argv et execArgv.

worker.getEnvironmentData(key)

[Historique]

VersionModifications
v17.5.0, v16.15.0N’est plus expérimental.
v15.12.0, v14.18.0Ajouté dans : v15.12.0, v14.18.0
  • key <any> Toute valeur JavaScript arbitraire et clonable qui peut être utilisée comme clé <Map>.
  • Retourne : <any>

Au sein d’un thread de worker, worker.getEnvironmentData() retourne un clone des données passées au worker.setEnvironmentData() du thread de spawning. Chaque nouveau Worker reçoit automatiquement sa propre copie des données d’environnement.

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

worker.isMainThread

Ajouté dans : v10.5.0

Est true si ce code ne s’exécute pas à l’intérieur d’un thread Worker.

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

if (isMainThread) {
  // Ceci recharge le fichier courant à l’intérieur d’une instance Worker.
  new Worker(__filename);
} else {
  console.log('Inside Worker!');
  console.log(isMainThread);  // Affiche 'false'.
}

worker.markAsUntransferable(object)

Ajouté dans : v14.5.0, v12.19.0

  • object <any> Toute valeur JavaScript arbitraire.

Marque un objet comme non transférable. Si object se trouve dans la liste de transfert d’un appel port.postMessage(), une erreur est levée. Il s’agit d’une no-op si object est une valeur primitive.

En particulier, cela a du sens pour les objets qui peuvent être clonés, plutôt que transférés, et qui sont utilisés par d’autres objets du côté de l’envoi. Par exemple, Node.js marque les ArrayBuffers qu’il utilise pour son Buffer pool avec ceci.

Cette opération ne peut pas être annulée.

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 {
  // Ceci lèvera une erreur, car pooledBuffer n’est pas transférable.
  port1.postMessage(typedArray1, [ typedArray1.buffer ]);
} catch (error) {
  // error.name === 'DataCloneError'
}

// La ligne suivante affiche le contenu de typedArray1 -- il possède toujours
// sa mémoire et n’a pas été transféré. Sans
// `markAsUntransferable()`, ceci afficherait un Uint8Array vide et l’appel
// postMessage aurait réussi.
// typedArray2 est également intact.
console.log(typedArray1);
console.log(typedArray2);

Il n’y a pas d’équivalent à cette API dans les navigateurs.

worker.isMarkedAsUntransferable(object)

Ajouté dans: v21.0.0

Vérifie si un objet est marqué comme non transférable avec markAsUntransferable().

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

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

isMarkedAsUntransferable(pooledBuffer);  // Retourne true.

Il n'y a pas d'équivalent à cette API dans les navigateurs.

worker.markAsUncloneable(object)

Ajouté dans: v23.0.0

  • object <any> Toute valeur JavaScript arbitraire.

Marque un objet comme non clonable. Si object est utilisé comme message dans un appel à port.postMessage(), une erreur est levée. Ceci est une no-op si object est une valeur primitive.

Cela n'a aucun effet sur ArrayBuffer, ou tout objet de type Buffer.

Cette opération est irréversible.

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

const anyObject = { foo: 'bar' };
markAsUncloneable(anyObject);
const { port1 } = new MessageChannel();
try {
  // Ceci lèvera une erreur, car anyObject n'est pas clonable.
  port1.postMessage(anyObject);
} catch (error) {
  // error.name === 'DataCloneError'
}

Il n'y a pas d'équivalent à cette API dans les navigateurs.

worker.moveMessagePortToContext(port, contextifiedSandbox)

Ajouté dans: v11.13.0

Transfère un MessagePort vers un vm Context différent. L'objet port d'origine est rendu inutilisable, et l'instance MessagePort retournée prend sa place.

Le MessagePort retourné est un objet dans le contexte cible et hérite de sa classe globale Object. Les objets passés à l'écouteur port.onmessage() sont également créés dans le contexte cible et héritent de sa classe globale Object.

Cependant, le MessagePort créé n'hérite plus de EventTarget, et seul port.onmessage() peut être utilisé pour recevoir des événements en l'utilisant.

worker.parentPort

Ajouté dans : v10.5.0

Si ce thread est un Worker, il s'agit d'un MessagePort permettant la communication avec le thread parent. Les messages envoyés à l'aide de parentPort.postMessage() sont disponibles dans le thread parent à l'aide de worker.on('message'), et les messages envoyés depuis le thread parent à l'aide de worker.postMessage() sont disponibles dans ce thread à l'aide de 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);  // Affiche 'Hello, world !'.
  });
  worker.postMessage('Hello, world !');
} else {
  // Lorsque qu'un message provenant du thread parent est reçu, le renvoyer :
  parentPort.once('message', (message) => {
    parentPort.postMessage(message);
  });
}

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

Ajouté dans : v22.5.0

[Stable: 1 - Expérimental]

Stable: 1 Stable: 1 - Développement actif

Envoie une valeur à un autre worker, identifié par son ID de thread.

Si le thread cible n'a pas d'écouteur pour l'événement workerMessage, l'opération émettra une erreur ERR_WORKER_MESSAGING_FAILED.

Si le thread cible a émis une erreur lors du traitement de l'événement workerMessage, l'opération émettra une erreur ERR_WORKER_MESSAGING_ERRORED.

Cette méthode doit être utilisée lorsque le thread cible n'est pas le parent ou l'enfant direct du thread actuel. Si les deux threads sont parent-enfant, utilisez require('node:worker_threads').parentPort.postMessage() et worker.postMessage() pour que les threads communiquent.

L'exemple ci-dessous montre l'utilisation de postMessageToThread : il crée 10 threads imbriqués, le dernier essaiera de communiquer avec le thread 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)

[Historique]

VersionModifications
v15.12.0L'argument port peut maintenant également faire référence à un BroadcastChannel.
v12.3.0Ajouté dans : v12.3.0

Reçoit un seul message d'un MessagePort donné. Si aucun message n'est disponible, undefined est retourné, sinon un objet avec une seule propriété message qui contient la charge utile du message, correspondant au message le plus ancien dans la queue du MessagePort.

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

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

Lorsque cette fonction est utilisée, aucun événement 'message' n'est émis et l'écouteur onmessage n'est pas invoqué.

worker.resourceLimits

Ajouté dans : v13.2.0, v12.16.0

Fournit l'ensemble des contraintes de ressources du moteur JS à l'intérieur de ce thread Worker. Si l'option resourceLimits a été passée au constructeur Worker, cela correspond à ses valeurs.

Si ceci est utilisé dans le thread principal, sa valeur est un objet vide.

worker.SHARE_ENV

Ajouté dans : v11.14.0

Une valeur spéciale qui peut être passée comme option env du constructeur Worker, pour indiquer que le thread courant et le thread Worker doivent partager l'accès en lecture et en écriture au même ensemble de variables d'environnement.

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

worker.setEnvironmentData(key[, value])

[Historique]

VersionModifications
v17.5.0, v16.15.0N'est plus expérimental.
v15.12.0, v14.18.0Ajouté dans : v15.12.0, v14.18.0
  • key <any> N'importe quelle valeur JavaScript arbitraire et clonable qui peut être utilisée comme clé <Map>.
  • value <any> N'importe quelle valeur JavaScript arbitraire et clonable qui sera clonée et transmise automatiquement à toutes les nouvelles instances de Worker. Si value est passé comme undefined, toute valeur précédemment définie pour la key sera supprimée.

L'API worker.setEnvironmentData() définit le contenu de worker.getEnvironmentData() dans le thread courant et toutes les nouvelles instances Worker générées à partir du contexte courant.

worker.threadId

Ajouté dans : v10.5.0

Un identifiant entier pour le thread courant. Sur l'objet worker correspondant (s'il y en a un), il est disponible en tant que worker.threadId. Cette valeur est unique pour chaque instance de Worker au sein d'un même processus.

worker.workerData

Ajouté dans la version : v10.5.0

Une valeur JavaScript arbitraire qui contient un clone des données transmises au constructeur Worker de ce thread.

Les données sont clonées comme si elles utilisaient postMessage(), conformément à l'algorithme de clonage structuré HTML.

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

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

Class : BroadcastChannel extends EventTarget

[Historique]

VersionModifications
v18.0.0N'est plus expérimental.
v15.4.0Ajouté dans la version : v15.4.0

Les instances de BroadcastChannel permettent une communication asynchrone un-à-plusieurs avec toutes les autres instances de BroadcastChannel liées au même nom 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('hello from every worker');
  bc.close();
}

new BroadcastChannel(name)

Ajouté dans la version : v15.4.0

  • name <any> Le nom du canal auquel se connecter. Toute valeur JavaScript qui peut être convertie en chaîne de caractères à l'aide de ${name} est autorisée.

broadcastChannel.close()

Ajouté dans la version : v15.4.0

Ferme la connexion BroadcastChannel.

broadcastChannel.onmessage

Ajouté dans la version : v15.4.0

  • Type : <Function> Invoquée avec un seul argument MessageEvent lorsqu'un message est reçu.

broadcastChannel.onmessageerror

Ajouté dans : v15.4.0

  • Type : <Function> Invoqué lorsqu’un message reçu ne peut pas être désérialisé.

broadcastChannel.postMessage(message)

Ajouté dans : v15.4.0

  • message <any> Toute valeur JavaScript clonable.

broadcastChannel.ref()

Ajouté dans : v15.4.0

Contraire de unref(). Appeler ref() sur un BroadcastChannel précédemment unref() ne permet pas au programme de se fermer s’il s’agit du seul descripteur actif restant (le comportement par défaut). Si le port est ref()é, appeler ref() à nouveau n’a aucun effet.

broadcastChannel.unref()

Ajouté dans : v15.4.0

Appeler unref() sur un BroadcastChannel permet au thread de se fermer si c’est le seul descripteur actif dans le système d’événements. Si le BroadcastChannel est déjà unref()é, appeler unref() à nouveau n’a aucun effet.

Class : MessageChannel

Ajouté dans : v10.5.0

Les instances de la classe worker.MessageChannel représentent un canal de communication bidirectionnel et asynchrone. Le MessageChannel n’a pas de méthodes propres. new MessageChannel() produit un objet avec les propriétés port1 et port2, qui font référence aux instances MessagePort liées.

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

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

Classe : MessagePort

[Historique]

VersionModifications
v14.7.0Cette classe hérite maintenant de EventTarget au lieu de EventEmitter.
v10.5.0Ajouté dans : v10.5.0

Les instances de la classe worker.MessagePort représentent une extrémité d’un canal de communication bidirectionnel et asynchrone. Il peut être utilisé pour transférer des données structurées, des régions de mémoire et d’autres MessagePorts entre différents Workers.

Cette implémentation correspond aux MessagePorts du navigateur.

Événement : 'close'

Ajouté dans : v10.5.0

L’événement 'close' est émis une fois que l’un des côtés du canal a été déconnecté.

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

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

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

Événement : 'message'

Ajouté dans : v10.5.0

  • value <any> La valeur transmise

L’événement 'message' est émis pour tout message entrant, contenant l’entrée clonée de port.postMessage().

Les auditeurs de cet événement reçoivent un clone du paramètre value tel qu’il est passé à postMessage() et aucun autre argument.

Événement : 'messageerror'

Ajouté dans : v14.5.0, v12.19.0

L’événement 'messageerror' est émis lorsque la désérialisation d’un message a échoué.

Actuellement, cet événement est émis lorsqu’une erreur se produit lors de l’instanciation de l’objet JS publié à la réception. De telles situations sont rares, mais peuvent se produire, par exemple, lorsque certains objets API Node.js sont reçus dans un vm.Context (où les API Node.js ne sont actuellement pas disponibles).

port.close()

Ajouté dans : v10.5.0

Désactive l’envoi ultérieur de messages de chaque côté de la connexion. Cette méthode peut être appelée lorsqu’aucune autre communication n’aura lieu sur ce MessagePort.

L’événement 'close' est émis sur les deux instances MessagePort qui font partie du canal.

port.postMessage(value[, transferList])

[Historique]

VersionModifications
v21.0.0Une erreur est levée lorsqu’un objet non transférable figure dans la liste de transfert.
v15.6.0Ajout de X509Certificate à la liste des types clonables.
v15.0.0Ajout de CryptoKey à la liste des types clonables.
v15.14.0, v14.18.0Ajout de 'BlockList' à la liste des types clonables.
v15.9.0, v14.18.0Ajout des types 'Histogram' à la liste des types clonables.
v14.5.0, v12.19.0Ajout de KeyObject à la liste des types clonables.
v14.5.0, v12.19.0Ajout de FileHandle à la liste des types transférables.
v10.5.0Ajouté dans : v10.5.0

Envoie une valeur JavaScript au côté récepteur de ce canal. value est transférée d’une manière compatible avec l’algorithme de clonage structuré HTML.

En particulier, les différences significatives par rapport à JSON sont les suivantes :

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 peut être une liste d’objets ArrayBuffer, MessagePort et FileHandle. Après le transfert, ils ne sont plus utilisables du côté de l’envoi du canal (même s’ils ne sont pas contenus dans value). Contrairement aux processus enfants, le transfert de handles tels que les sockets réseau n’est actuellement pas pris en charge.

Si value contient des instances SharedArrayBuffer, celles-ci sont accessibles depuis les deux threads. Ils ne peuvent pas être répertoriés dans transferList.

value peut toujours contenir des instances ArrayBuffer qui ne sont pas dans transferList ; dans ce cas, la mémoire sous-jacente est copiée plutôt que déplacée.

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 ]);
// Ceci publie une copie de `uint8Array` :
port2.postMessage(uint8Array);
// Ceci ne copie pas les données, mais rend `uint8Array` inutilisable :
port2.postMessage(uint8Array, [ uint8Array.buffer ]);

// La mémoire de `sharedUint8Array` est accessible à la fois depuis
// l’original et la copie reçue par `.on('message')` :
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4));
port2.postMessage(sharedUint8Array);

// Ceci transfère un port de message fraîchement créé au destinataire.
// Ceci peut être utilisé, par exemple, pour créer des canaux de communication entre
// plusieurs threads `Worker` qui sont des enfants du même thread parent.
const otherChannel = new MessageChannel();
port2.postMessage({ port: otherChannel.port1 }, [ otherChannel.port1 ]);

L’objet message est cloné immédiatement et peut être modifié après la publication sans avoir d’effets secondaires.

Pour plus d’informations sur les mécanismes de sérialisation et de désérialisation derrière cette API, consultez l’API de sérialisation du module node:v8.

Considérations lors du transfert de TypedArrays et de Buffers

Toutes les instances TypedArray et Buffer sont des vues sur un ArrayBuffer sous-jacent. C'est-à-dire que c'est le ArrayBuffer qui stocke réellement les données brutes, tandis que les objets TypedArray et Buffer fournissent un moyen de visualiser et de manipuler les données. Il est possible et courant de créer plusieurs vues sur la même instance de ArrayBuffer. Il faut faire très attention lors de l'utilisation d'une liste de transfert pour transférer un ArrayBuffer, car cela rend inutilisables toutes les instances TypedArray et Buffer qui partagent le même ArrayBuffer.

js
const ab = new ArrayBuffer(10);

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

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

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

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

Pour les instances Buffer en particulier, la possibilité de transférer ou de cloner le ArrayBuffer sous-jacent dépend entièrement de la façon dont les instances ont été créées, ce qui souvent ne peut pas être déterminé de manière fiable.

Un ArrayBuffer peut être marqué avec markAsUntransferable() pour indiquer qu'il doit toujours être cloné et jamais transféré.

Selon la façon dont une instance Buffer a été créée, elle peut ou non posséder son ArrayBuffer sous-jacent. Un ArrayBuffer ne doit pas être transféré à moins qu'il ne soit connu que l'instance Buffer le possède. En particulier, pour les Buffer créés à partir du pool interne de Buffer (en utilisant, par exemple, Buffer.from() ou Buffer.allocUnsafe()), leur transfert n'est pas possible et ils sont toujours clonés, ce qui envoie une copie de l'ensemble du pool de Buffer. Ce comportement peut entraîner une utilisation de la mémoire plus élevée et des problèmes de sécurité potentiels.

Voir Buffer.allocUnsafe() pour plus de détails sur le pooling de Buffer.

Les ArrayBuffer pour les instances Buffer créées à l'aide de Buffer.alloc() ou Buffer.allocUnsafeSlow() peuvent toujours être transférés, mais cela rend inutilisables toutes les autres vues existantes de ces ArrayBuffer.

Considérations lors du clonage d'objets avec prototypes, classes et accesseurs

Étant donné que le clonage d'objets utilise l'algorithme de clonage structuré HTML, les propriétés non énumérables, les accesseurs de propriétés et les prototypes d'objets ne sont pas conservés. En particulier, les objets Buffer seront lus comme de simples Uint8Array du côté réception, et les instances de classes JavaScript seront clonées comme de simples objets 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 }

Cette limitation s'étend à de nombreux objets intégrés, tels que l'objet global URL :

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

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

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

// Prints: { }

port.hasRef()

Ajouté dans : v18.1.0, v16.17.0

[Stable: 1 - Expérimental]

Stable: 1 Stabilité : 1 - Expérimental

Si la valeur est true, l'objet MessagePort maintiendra la boucle d'événement Node.js active.

port.ref()

Ajouté dans : v10.5.0

Contraire de unref(). L'appel de ref() sur un port précédemment unref() ne permet pas au programme de se terminer s'il s'agit du seul handle actif restant (le comportement par défaut). Si le port est ref(), appeler à nouveau ref() n'a aucun effet.

Si des listeners sont attachés ou supprimés à l'aide de .on('message'), le port est ref() et unref() automatiquement selon qu'il existe ou non des listeners pour l'événement.

port.start()

Ajouté dans : v10.5.0

Commence à recevoir des messages sur ce MessagePort. Lorsque ce port est utilisé comme émetteur d’événements, cette méthode est appelée automatiquement une fois que les listeners 'message' sont attachés.

Cette méthode existe pour assurer la parité avec l’API Web MessagePort. Dans Node.js, elle n’est utile que pour ignorer les messages lorsqu’aucun écouteur d’événements n’est présent. Node.js diverge également dans sa gestion de .onmessage. La définition de celui-ci appelle automatiquement .start(), mais sa suppression permet de mettre les messages en file d’attente jusqu’à ce qu’un nouveau gestionnaire soit défini ou que le port soit supprimé.

port.unref()

Ajouté dans : v10.5.0

Appeler unref() sur un port permet au thread de se fermer si c’est la seule handle active dans le système d’événements. Si le port est déjà unref()ed, appeler unref() à nouveau n’a aucun effet.

Si des listeners sont attachés ou supprimés à l’aide de .on('message'), le port est ref()ed et unref()ed automatiquement selon qu’il existe ou non des listeners pour l’événement.

Classe : Worker

Ajouté dans : v10.5.0

La classe Worker représente un thread d’exécution JavaScript indépendant. La plupart des API Node.js y sont disponibles.

Les différences notables dans un environnement Worker sont :

  • Les flux process.stdin, process.stdout et process.stderr peuvent être redirigés par le thread parent.
  • La propriété require('node:worker_threads').isMainThread est définie sur false.
  • Le port de message require('node:worker_threads').parentPort est disponible.
  • process.exit() n’arrête pas l’ensemble du programme, mais seulement le thread unique, et process.abort() n’est pas disponible.
  • process.chdir() et les méthodes process qui définissent les identifiants de groupe ou d’utilisateur ne sont pas disponibles.
  • process.env est une copie des variables d’environnement du thread parent, sauf indication contraire. Les modifications apportées à une copie ne sont pas visibles dans les autres threads et ne sont pas visibles par les modules complémentaires natifs (sauf si worker.SHARE_ENV est transmis en tant qu’option env au constructeur Worker). Sous Windows, contrairement au thread principal, une copie des variables d’environnement fonctionne en tenant compte de la casse.
  • process.title ne peut pas être modifié.
  • Les signaux ne sont pas transmis via process.on('...').
  • L’exécution peut s’arrêter à tout moment à la suite de l’invocation de worker.terminate().
  • Les canaux IPC des processus parents ne sont pas accessibles.
  • Le module trace_events n’est pas pris en charge.
  • Les modules complémentaires natifs ne peuvent être chargés à partir de plusieurs threads que s’ils remplissent certaines conditions.

La création d’instances Worker à l’intérieur d’autres Workers est possible.

Comme les Web Workers et le node:cluster module, la communication bidirectionnelle peut être réalisée par le biais du transfert de messages entre threads. En interne, un Worker possède une paire intégrée de MessagePorts qui sont déjà associées l’une à l’autre lorsque le Worker est créé. Bien que l’objet MessagePort du côté parent ne soit pas directement exposé, ses fonctionnalités sont exposées via worker.postMessage() et l’événement worker.on('message') sur l’objet Worker pour le thread parent.

Pour créer des canaux de messagerie personnalisés (ce qui est encouragé par rapport à l’utilisation du canal global par défaut, car cela facilite la séparation des préoccupations), les utilisateurs peuvent créer un objet MessageChannel sur l’un ou l’autre thread et transmettre l’un des MessagePorts sur ce MessageChannel à l’autre thread via un canal préexistant, tel que le canal global.

Voir port.postMessage() pour plus d’informations sur la façon dont les messages sont transmis, et quel type de valeurs JavaScript peut être transporté avec succès à travers la barrière des threads.

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

[Historique]

VersionModifications
v19.8.0, v18.16.0Ajout du support d'une option name, qui permet d'ajouter un nom au titre du worker pour le débogage.
v14.9.0Le paramètre filename peut être un objet URL WHATWG utilisant le protocole data:.
v14.9.0L'option trackUnmanagedFds a été définie sur true par défaut.
v14.6.0, v12.19.0L'option trackUnmanagedFds a été introduite.
v13.13.0, v12.17.0L'option transferList a été introduite.
v13.12.0, v12.17.0Le paramètre filename peut être un objet URL WHATWG utilisant le protocole file:.
v13.4.0, v12.16.0L'option argv a été introduite.
v13.2.0, v12.16.0L'option resourceLimits a été introduite.
v10.5.0Ajoutée dans : v10.5.0
  • filename <string> | <URL> Le chemin d'accès au script principal ou au module du Worker. Doit être soit un chemin absolu, soit un chemin relatif (c'est-à-dire relatif au répertoire de travail actuel) commençant par ./ ou ../, soit un objet URL WHATWG utilisant le protocole file: ou data:. Lors de l'utilisation d'une data: URL, les données sont interprétées en fonction du type MIME à l'aide du chargeur de module ECMAScript. Si options.eval est true, il s'agit d'une chaîne contenant du code JavaScript plutôt qu'un chemin d'accès.
  • options <Object>
    • argv <any[]> Liste des arguments qui seraient transformés en chaînes de caractères et ajoutés à process.argv dans le worker. Ceci est très similaire à workerData mais les valeurs sont disponibles sur le process.argv global comme si elles étaient passées comme options CLI au script.

    • env <Object> Si défini, spécifie la valeur initiale de process.env à l'intérieur du thread Worker. En tant que valeur spéciale, worker.SHARE_ENV peut être utilisé pour spécifier que le thread parent et le thread enfant doivent partager leurs variables d'environnement ; dans ce cas, les modifications apportées à l'objet process.env d'un thread affectent également l'autre thread. Par défaut : process.env.

    • eval <boolean> Si true et que le premier argument est une string, interprète le premier argument du constructeur comme un script qui est exécuté une fois que le worker est en ligne.

    • execArgv <string[]> Liste des options CLI de node transmises au worker. Les options V8 (telles que --max-old-space-size) et les options qui affectent le processus (telles que --title) ne sont pas prises en charge. Si elle est définie, elle est fournie en tant que process.execArgv à l'intérieur du worker. Par défaut, les options sont héritées du thread parent.

    • stdin <boolean> Si cette option est définie sur true, alors worker.stdin fournit un flux accessible en écriture dont le contenu apparaît comme process.stdin à l'intérieur du Worker. Par défaut, aucune donnée n'est fournie.

    • stdout <boolean> Si cette option est définie sur true, alors worker.stdout n'est pas automatiquement redirigé vers process.stdout dans le parent.

    • stderr <boolean> Si cette option est définie sur true, alors worker.stderr n'est pas automatiquement redirigé vers process.stderr dans le parent.

    • workerData <any> Toute valeur JavaScript qui est clonée et rendue disponible en tant que require('node:worker_threads').workerData. Le clonage se produit comme décrit dans l'algorithme de clonage structuré HTML, et une erreur est levée si l'objet ne peut pas être cloné (par exemple, parce qu'il contient des functions).

    • trackUnmanagedFds <boolean> Si cette option est définie sur true, alors le Worker suit les descripteurs de fichiers bruts gérés via fs.open() et fs.close(), et les ferme lorsque le Worker se termine, de la même manière que d'autres ressources telles que les sockets réseau ou les descripteurs de fichiers gérés via l'API FileHandle. Cette option est automatiquement héritée par tous les Worker imbriqués. Par défaut : true.

    • transferList <Object[]> Si un ou plusieurs objets de type MessagePort sont passés dans workerData, une transferList est requise pour ces éléments ou ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST est levée. Voir port.postMessage() pour plus d'informations.

    • resourceLimits <Object> Un ensemble facultatif de limites de ressources pour la nouvelle instance de moteur JS. Atteindre ces limites entraîne la fin de l'instance Worker. Ces limites affectent uniquement le moteur JS, et aucune donnée externe, y compris aucun ArrayBuffer. Même si ces limites sont définies, le processus peut toujours s'arrêter s'il rencontre une situation globale de mémoire insuffisante.

    • maxOldGenerationSizeMb <number> La taille maximale du tas principal en Mo. Si l'argument de ligne de commande --max-old-space-size est défini, il remplace ce paramètre.

    • maxYoungGenerationSizeMb <number> La taille maximale d'un espace de tas pour les objets créés récemment. Si l'argument de ligne de commande --max-semi-space-size est défini, il remplace ce paramètre.

    • codeRangeSizeMb <number> La taille d'une plage de mémoire pré-allouée utilisée pour le code généré.

    • stackSizeMb <number> La taille maximale par défaut de la pile pour le thread. De petites valeurs peuvent entraîner des instances Worker inutilisables. Par défaut : 4.

    • name <string> Un name facultatif à ajouter au titre du worker à des fins de débogage/identification, ce qui donne le titre final sous la forme [worker ${id}] ${name}. Par défaut : ''.

Événement : 'error'

Ajouté dans : v10.5.0

L'événement 'error' est émis si le thread worker lève une exception non interceptée. Dans ce cas, le worker est terminé.

Événement : 'exit'

Ajouté dans : v10.5.0

L'événement 'exit' est émis une fois que le worker s'est arrêté. Si le worker s'est arrêté en appelant process.exit(), le paramètre exitCode est le code de sortie transmis. Si le worker a été terminé, le paramètre exitCode est 1.

Il s'agit de l'événement final émis par toute instance de Worker.

Événement : 'message'

Ajouté dans : v10.5.0

  • value <any> La valeur transmise

L'événement 'message' est émis lorsque le thread worker a invoqué require('node:worker_threads').parentPort.postMessage(). Voir l'événement port.on('message') pour plus de détails.

Tous les messages envoyés depuis le thread worker sont émis avant que l''exit' event ne soit émis sur l'objet Worker.

Événement : 'messageerror'

Ajouté dans : v14.5.0, v12.19.0

L'événement 'messageerror' est émis lorsque la désérialisation d'un message a échoué.

Événement : 'online'

Ajouté dans : v10.5.0

L'événement 'online' est émis lorsque le thread worker a commencé à exécuter du code JavaScript.

worker.getHeapSnapshot([options])

[Historique]

VersionModifications
v19.1.0Prise en charge des options pour configurer le heap snapshot.
v13.9.0, v12.17.0Ajouté dans : v13.9.0, v12.17.0
  • options <Object>

    • exposeInternals <boolean> Si true, expose les éléments internes dans le heap snapshot. Par défaut : false.
    • exposeNumericValues <boolean> Si true, expose les valeurs numériques dans des champs artificiels. Par défaut : false.
  • Retourne : <Promise> Une promesse pour un Readable Stream contenant un heap snapshot V8

Retourne un flux lisible pour un snapshot V8 de l'état actuel du Worker. Voir v8.getHeapSnapshot() pour plus de détails.

Si le thread Worker ne s'exécute plus, ce qui peut se produire avant que l'événement 'exit' event ne soit émis, la Promise retournée est rejetée immédiatement avec une erreur ERR_WORKER_NOT_RUNNING.

worker.performance

Ajouté dans : v15.1.0, v14.17.0, v12.22.0

Un objet qui peut être utilisé pour interroger les informations de performance d'une instance de worker. Semblable à perf_hooks.performance.

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

Ajouté dans : v15.1.0, v14.17.0, v12.22.0

  • utilization1 <Object> Le résultat d'un appel précédent à eventLoopUtilization().
  • utilization2 <Object> Le résultat d'un appel précédent à eventLoopUtilization() avant utilization1.
  • Retourne : <Object>

Le même appel que perf_hooks eventLoopUtilization(), sauf que les valeurs de l'instance du worker sont retournées.

Une différence est que, contrairement au thread principal, l'amorçage dans un worker se fait dans la boucle d'événements. Ainsi, l'utilisation de la boucle d'événements est immédiatement disponible une fois que le script du worker commence son exécution.

Un temps idle qui n'augmente pas n'indique pas que le worker est bloqué dans l'amorçage. Les exemples suivants montrent comment la durée de vie entière du worker n'accumule jamais de temps idle, mais est toujours en mesure de traiter les messages.

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'utilisation de la boucle d'événements d'un worker n'est disponible qu'après l'émission de l' 'online' event, et si elle est appelée avant cela, ou après l' 'exit' event, alors toutes les propriétés ont la valeur 0.

worker.postMessage(value[, transferList])

Ajouté dans : v10.5.0

Envoie un message au worker qui est reçu via require('node:worker_threads').parentPort.on('message'). Voir port.postMessage() pour plus de détails.

worker.ref()

Ajouté dans : v10.5.0

Contrairement à unref(), l'appel de ref() sur un worker précédemment unref() ne permet pas au programme de se terminer s'il s'agit du seul gestionnaire actif restant (le comportement par défaut). Si le worker est ref()é, appeler ref() à nouveau n'a aucun effet.

worker.resourceLimits

Ajouté dans : v13.2.0, v12.16.0

Fournit l'ensemble des contraintes de ressources du moteur JS pour ce thread Worker. Si l'option resourceLimits a été passée au constructeur Worker, cela correspond à ses valeurs.

Si le worker s'est arrêté, la valeur de retour est un objet vide.

worker.stderr

Ajouté dans : v10.5.0

Il s'agit d'un flux lisible qui contient les données écrites dans process.stderr à l'intérieur du thread worker. Si stderr: true n'a pas été passé au constructeur Worker, alors les données sont transmises au flux process.stderr du thread parent.

worker.stdin

Ajouté dans: v10.5.0

Si stdin: true a été passé au constructeur Worker, il s'agit d'un flux accessible en écriture. Les données écrites dans ce flux seront disponibles dans le thread worker en tant que process.stdin.

worker.stdout

Ajouté dans: v10.5.0

Il s'agit d'un flux accessible en lecture qui contient les données écrites dans process.stdout à l'intérieur du thread worker. Si stdout: true n'a pas été passé au constructeur Worker, alors les données sont redirigées vers le flux process.stdout du thread parent.

worker.terminate()

[Historique]

VersionModifications
v12.5.0Cette fonction renvoie désormais une Promise. Le passage d'un rappel est obsolète et était inutile jusqu'à cette version, car le Worker était en fait arrêté de manière synchrone. L'arrêt est désormais une opération entièrement asynchrone.
v10.5.0Ajouté dans: v10.5.0

Arrête l'exécution de tout JavaScript dans le thread worker dès que possible. Renvoie une Promise pour le code de sortie qui est résolue lorsque l'événement 'exit' event est émis.

worker.threadId

Ajouté dans: v10.5.0

Un identifiant entier pour le thread référencé. À l'intérieur du thread worker, il est disponible en tant que require('node:worker_threads').threadId. Cette valeur est unique pour chaque instance Worker à l'intérieur d'un seul processus.

worker.unref()

Ajouté dans: v10.5.0

Appeler unref() sur un worker permet au thread de se terminer si c'est le seul handle actif dans le système d'événements. Si le worker est déjà unref()é, appeler à nouveau unref() n'a aucun effet.

Notes

Blocage synchrone de stdio

Les Workers utilisent le passage de messages via <MessagePort> pour implémenter les interactions avec stdio. Cela signifie que la sortie stdio provenant d'un Worker peut être bloquée par du code synchrone du côté du destinataire qui bloque la boucle d'événements 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++) {
    // Boucle pour simuler du travail.
  }
} else {
  // Cette sortie sera bloquée par la boucle for dans le thread 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++) {
    // Boucle pour simuler du travail.
  }
} else {
  // Cette sortie sera bloquée par la boucle for dans le thread principal.
  console.log('foo');
}

Lancement de threads worker à partir de scripts de préchargement

Soyez prudent lors du lancement de threads worker à partir de scripts de préchargement (scripts chargés et exécutés à l'aide de l'indicateur de ligne de commande -r). À moins que l'option execArgv ne soit explicitement définie, les nouveaux threads Worker héritent automatiquement des indicateurs de ligne de commande du processus en cours d'exécution et préchargeront les mêmes scripts de préchargement que le thread principal. Si le script de préchargement lance inconditionnellement un thread worker, chaque thread engendré engendrera un autre jusqu'à ce que l'application plante.