Worker-Threads#

Das node:worker_threads-Modul ermöglicht die Verwendung von Threads, die JavaScript parallel ausführen. Um darauf zuzugreifen

import worker_threads from 'node:worker_threads';
'use strict';

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

Worker (Threads) sind nützlich für CPU-intensive JavaScript-Operationen. Bei I/O-intensiven Aufgaben helfen sie wenig. Die eingebauten asynchronen I/O-Operationen von Node.js sind effizienter als Worker es sein können.

Im Gegensatz zu child_process oder cluster können worker_threads Arbeitsspeicher teilen. Dies geschieht durch das Übertragen von ArrayBuffer-Instanzen oder das Teilen von SharedArrayBuffer-Instanzen.

import {
  Worker,
  isMainThread,
  parentPort,
  workerData,
} from 'node:worker_threads';

if (!isMainThread) {
  const { parse } = await import('some-js-parsing-library');
  const script = workerData;
  parentPort.postMessage(parse(script));
}

export default function parseJSAsync(script) {
  return new Promise((resolve, reject) => {
    const worker = new Worker(new URL(import.meta.url), {
      workerData: script,
    });
    worker.on('message', resolve);
    worker.once('error', reject);
    worker.once('exit', (code) => {
      if (code !== 0)
        reject(new Error(`Worker stopped with exit code ${code}`));
    });
  });
};
'use strict';

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.once('error', reject);
      worker.once('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));
}

Das obige Beispiel startet einen Worker-Thread für jeden parseJSAsync()-Aufruf. In der Praxis sollte für solche Aufgaben ein Pool von Workern verwendet werden. Andernfalls würde der Overhead beim Erstellen von Workern wahrscheinlich den Nutzen überwiegen.

Verwenden Sie bei der Implementierung eines Worker-Pools die AsyncResource-API, um Diagnosetools (z. B. zur Bereitstellung asynchroner Stack-Traces) über die Korrelation zwischen Aufgaben und deren Ergebnissen zu informieren. Siehe "Using AsyncResource for a Worker thread pool" in der async_hooks-Dokumentation für eine Beispielimplementierung.

Worker-Threads erben standardmäßig keine prozessspezifischen Optionen. Siehe Worker-Konstruktor-Optionen, um zu erfahren, wie Worker-Thread-Optionen, insbesondere argv- und execArgv-Optionen, angepasst werden können.

worker_threads.getEnvironmentData(key)#

  • key <any> Jeder beliebige, klonbare JavaScript-Wert, der als <Map>-Schlüssel verwendet werden kann.
  • Gibt zurück: <any>

Innerhalb eines Worker-Threads gibt worker.getEnvironmentData() einen Klon der Daten zurück, die an worker.setEnvironmentData() des erzeugenden Threads übergeben wurden. Jeder neue Worker erhält automatisch seine eigene Kopie der Umgebungsdaten.

import {
  Worker,
  isMainThread,
  setEnvironmentData,
  getEnvironmentData,
} from 'node:worker_threads';

if (isMainThread) {
  setEnvironmentData('Hello', 'World!');
  const worker = new Worker(new URL(import.meta.url));
} else {
  console.log(getEnvironmentData('Hello'));  // Prints 'World!'.
}
'use strict';

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

if (isMainThread) {
  setEnvironmentData('Hello', 'World!');
  const worker = new Worker(__filename);
} else {
  console.log(getEnvironmentData('Hello'));  // Prints 'World!'.
}

worker_threads.isInternalThread#

Ist true, wenn dieser Code innerhalb eines internen Worker-Threads läuft (z. B. der Loader-Thread).

// loader.js
import { isInternalThread } from 'node:worker_threads';
console.log(isInternalThread);  // true
// loader.js
'use strict';

const { isInternalThread } = require('node:worker_threads');
console.log(isInternalThread);  // true
// main.js
import { isInternalThread } from 'node:worker_threads';
console.log(isInternalThread);  // false
// main.js
'use strict';

const { isInternalThread } = require('node:worker_threads');
console.log(isInternalThread);  // false

worker_threads.isMainThread#

Ist true, wenn dieser Code nicht innerhalb eines Worker-Threads läuft.

import { Worker, isMainThread } from 'node:worker_threads';

if (isMainThread) {
  // This re-loads the current file inside a Worker instance.
  new Worker(new URL(import.meta.url));
} else {
  console.log('Inside Worker!');
  console.log(isMainThread);  // Prints 'false'.
}
'use strict';

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

if (isMainThread) {
  // This re-loads the current file inside a Worker instance.
  new Worker(__filename);
} else {
  console.log('Inside Worker!');
  console.log(isMainThread);  // Prints 'false'.
}

worker_threads.markAsUntransferable(object)#

  • object <any> Ein beliebiger JavaScript-Wert.

Markiert ein Objekt als nicht übertragbar. Wenn object in der Transferliste eines port.postMessage()-Aufrufs vorkommt, wird ein Fehler ausgelöst. Dies ist ein No-Op, wenn object ein primitiver Wert ist.

Dies ist insbesondere sinnvoll für Objekte, die eher geklont als übertragen werden können und die von anderen Objekten auf der sendenden Seite verwendet werden. Beispielsweise markiert Node.js die ArrayBuffers, die es für seinen Buffer-Pool verwendet, damit. ArrayBuffer.prototype.transfer() ist für solche Array-Buffer-Instanzen nicht zulässig.

Dieser Vorgang kann nicht rückgängig gemacht werden.

import { MessageChannel, markAsUntransferable } from '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 {
  // This will throw an error, because pooledBuffer is not transferable.
  port1.postMessage(typedArray1, [ typedArray1.buffer ]);
} catch (error) {
  // error.name === 'DataCloneError'
}

// The following line prints the contents of typedArray1 -- it still owns
// its memory and has not been transferred. Without
// `markAsUntransferable()`, this would print an empty Uint8Array and the
// postMessage call would have succeeded.
// typedArray2 is intact as well.
console.log(typedArray1);
console.log(typedArray2);
'use strict';

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 {
  // This will throw an error, because pooledBuffer is not transferable.
  port1.postMessage(typedArray1, [ typedArray1.buffer ]);
} catch (error) {
  // error.name === 'DataCloneError'
}

// The following line prints the contents of typedArray1 -- it still owns
// its memory and has not been transferred. Without
// `markAsUntransferable()`, this would print an empty Uint8Array and the
// postMessage call would have succeeded.
// typedArray2 is intact as well.
console.log(typedArray1);
console.log(typedArray2);

Es gibt kein Äquivalent zu dieser API in Browsern.

worker_threads.isMarkedAsUntransferable(object)#

  • object <any> Ein beliebiger JavaScript-Wert.
  • Rückgabewert: <boolean>

Überprüft, ob ein Objekt mit markAsUntransferable() als nicht übertragbar markiert wurde.

import { markAsUntransferable, isMarkedAsUntransferable } from 'node:worker_threads';

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

isMarkedAsUntransferable(pooledBuffer);  // Returns true.
'use strict';

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

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

isMarkedAsUntransferable(pooledBuffer);  // Returns true.

Es gibt kein Äquivalent zu dieser API in Browsern.

worker_threads.markAsUncloneable(object)#

  • object <any> Ein beliebiger JavaScript-Wert.

Markiert ein Objekt als nicht klonbar. Wenn object als message in einem port.postMessage()-Aufruf verwendet wird, wird ein Fehler ausgelöst. Dies ist ein No-Op, wenn object ein primitiver Wert ist.

Dies hat keine Auswirkungen auf ArrayBuffer oder beliebige Buffer-ähnliche Objekte.

Dieser Vorgang kann nicht rückgängig gemacht werden.

import { markAsUncloneable } from 'node:worker_threads';

const anyObject = { foo: 'bar' };
markAsUncloneable(anyObject);
const { port1 } = new MessageChannel();
try {
  // This will throw an error, because anyObject is not cloneable.
  port1.postMessage(anyObject);
} catch (error) {
  // error.name === 'DataCloneError'
}
'use strict';

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

const anyObject = { foo: 'bar' };
markAsUncloneable(anyObject);
const { port1 } = new MessageChannel();
try {
  // This will throw an error, because anyObject is not cloneable.
  port1.postMessage(anyObject);
} catch (error) {
  // error.name === 'DataCloneError'
}

Es gibt kein Äquivalent zu dieser API in Browsern.

worker_threads.moveMessagePortToContext(port, contextifiedSandbox)#

Überträgt einen MessagePort in einen anderen vm-Kontext. Das ursprüngliche port-Objekt wird unbrauchbar gemacht, und die zurückgegebene MessagePort-Instanz nimmt seinen Platz ein.

Der zurückgegebene MessagePort ist ein Objekt im Zielkontext und erbt von dessen globaler Object-Klasse. Objekte, die an den port.onmessage()-Listener übergeben werden, werden ebenfalls im Zielkontext erstellt und erben von dessen globaler Object-Klasse.

Der erstellte MessagePort erbt jedoch nicht mehr von <EventTarget>, und nur port.onmessage() kann verwendet werden, um Ereignisse darüber zu empfangen.

worker_threads.parentPort#

Wenn dieser Thread ein Worker ist, ist dies ein MessagePort, der die Kommunikation mit dem übergeordneten Thread ermöglicht. Nachrichten, die mit parentPort.postMessage() gesendet werden, sind im übergeordneten Thread mit worker.on('message') verfügbar, und Nachrichten, die vom übergeordneten Thread mit worker.postMessage() gesendet werden, sind in diesem Thread mit parentPort.on('message') verfügbar.

import { Worker, isMainThread, parentPort } from 'node:worker_threads';

if (isMainThread) {
  const worker = new Worker(new URL(import.meta.url));
  worker.once('message', (message) => {
    console.log(message);  // Prints 'Hello, world!'.
  });
  worker.postMessage('Hello, world!');
} else {
  // When a message from the parent thread is received, send it back:
  parentPort.once('message', (message) => {
    parentPort.postMessage(message);
  });
}
'use strict';

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

if (isMainThread) {
  const worker = new Worker(__filename);
  worker.once('message', (message) => {
    console.log(message);  // Prints 'Hello, world!'.
  });
  worker.postMessage('Hello, world!');
} else {
  // When a message from the parent thread is received, send it back:
  parentPort.once('message', (message) => {
    parentPort.postMessage(message);
  });
}

worker_threads.postMessageToThread(threadId, value[, transferList][, timeout])#

Stabilität: 1.1 - Aktive Entwicklung

  • threadId <number> Die ID des Ziel-Threads. Wenn die Thread-ID ungültig ist, wird ein ERR_WORKER_MESSAGING_FAILED-Fehler ausgelöst. Wenn die ID des Ziel-Threads die ID des aktuellen Threads ist, wird ein ERR_WORKER_MESSAGING_SAME_THREAD-Fehler ausgelöst.
  • value <any> Der zu sendende Wert.
  • transferList <Object[]> Wenn ein oder mehrere MessagePort-ähnliche Objekte in value übergeben werden, ist eine transferList für diese Elemente erforderlich, andernfalls wird ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST ausgelöst. Siehe port.postMessage() für weitere Informationen.
  • timeout <number> Zeit in Millisekunden, die auf die Zustellung der Nachricht gewartet werden soll. Standardmäßig ist dies undefined, was bedeutet, dass unendlich gewartet wird. Wenn der Vorgang abläuft, wird ein ERR_WORKER_MESSAGING_TIMEOUT-Fehler ausgelöst.
  • Gibt zurück: <Promise> Ein Promise, das erfüllt wird, wenn die Nachricht vom Ziel-Thread erfolgreich verarbeitet wurde.

Sendet einen Wert an einen anderen Worker, identifiziert durch seine Thread-ID.

Wenn der Ziel-Thread keinen Listener für das workerMessage-Ereignis hat, löst der Vorgang einen ERR_WORKER_MESSAGING_FAILED-Fehler aus.

Wenn der Ziel-Thread während der Verarbeitung des workerMessage-Ereignisses einen Fehler ausgelöst hat, löst der Vorgang einen ERR_WORKER_MESSAGING_ERRORED-Fehler aus.

Diese Methode sollte verwendet werden, wenn der Ziel-Thread nicht der direkte Eltern- oder Kind-Thread des aktuellen Threads ist. Wenn die beiden Threads Eltern-Kind-Beziehungen haben, verwenden Sie require('node:worker_threads').parentPort.postMessage() und worker.postMessage(), um die Threads kommunizieren zu lassen.

Das folgende Beispiel zeigt die Verwendung von postMessageToThread: Es erstellt 10 verschachtelte Threads, der letzte versucht, mit dem Haupt-Thread zu kommunizieren.

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(new URL(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;
'use strict';

const process = require('node:process');
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_threads.receiveMessageOnPort(port)#

Empfängt eine einzelne Nachricht von einem angegebenen MessagePort. Wenn keine Nachricht verfügbar ist, wird undefined zurückgegeben, andernfalls ein Objekt mit einer einzigen message-Eigenschaft, die die Nachrichten-Nutzlast enthält, entsprechend der ältesten Nachricht in der Warteschlange des MessagePorts.

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

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

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

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

Wenn diese Funktion verwendet wird, wird kein 'message'-Ereignis ausgegeben und der onmessage-Listener wird nicht aufgerufen.

worker_threads.resourceLimits#

Bietet die Menge der Ressourcenbeschränkungen der JS-Engine innerhalb dieses Worker-Threads. Wenn die Option resourceLimits an den Worker-Konstruktor übergeben wurde, entspricht dies dessen Werten.

Wenn dies im Haupt-Thread verwendet wird, ist sein Wert ein leeres Objekt.

worker_threads.SHARE_ENV#

Ein spezieller Wert, der als env-Option an den Worker-Konstruktor übergeben werden kann, um anzugeben, dass der aktuelle Thread und der Worker-Thread Lese- und Schreibzugriff auf denselben Satz von Umgebungsvariablen haben sollen.

import process from 'node:process';
import { Worker, SHARE_ENV } from 'node:worker_threads';
new Worker('process.env.SET_IN_WORKER = "foo"', { eval: true, env: SHARE_ENV })
  .once('exit', () => {
    console.log(process.env.SET_IN_WORKER);  // Prints 'foo'.
  });
'use strict';

const { Worker, SHARE_ENV } = require('node:worker_threads');
new Worker('process.env.SET_IN_WORKER = "foo"', { eval: true, env: SHARE_ENV })
  .once('exit', () => {
    console.log(process.env.SET_IN_WORKER);  // Prints 'foo'.
  });

worker_threads.setEnvironmentData(key[, value])#

  • key <any> Jeder beliebige, klonbare JavaScript-Wert, der als <Map>-Schlüssel verwendet werden kann.
  • value <any> Jeder beliebige, klonbare JavaScript-Wert, der geklont und automatisch an alle neuen Worker-Instanzen übergeben wird. Wenn value als undefined übergeben wird, wird jeder zuvor gesetzte Wert für den key gelöscht.

Die API worker.setEnvironmentData() setzt den Inhalt von worker.getEnvironmentData() im aktuellen Thread und allen neuen Worker-Instanzen, die aus dem aktuellen Kontext erzeugt werden.

worker_threads.threadId#

Eine Ganzzahl-Kennung für den aktuellen Thread. Auf dem entsprechenden Worker-Objekt (falls vorhanden) ist sie als worker.threadId verfügbar. Dieser Wert ist für jede Worker-Instanz innerhalb eines einzelnen Prozesses eindeutig.

worker_threads.threadName#

Ein String-Bezeichner für den aktuellen Thread oder null, wenn der Thread nicht läuft. Auf dem entsprechenden Worker-Objekt (falls vorhanden) ist er als worker.threadName verfügbar.

worker_threads.workerData#

Ein beliebiger JavaScript-Wert, der einen Klon der Daten enthält, die an den Worker-Konstruktor dieses Threads übergeben wurden.

Die Daten werden so geklont, als würde man postMessage() verwenden, gemäß dem HTML Structured Clone Algorithm.

import { Worker, isMainThread, workerData } from 'node:worker_threads';

if (isMainThread) {
  const worker = new Worker(new URL(import.meta.url), { workerData: 'Hello, world!' });
} else {
  console.log(workerData);  // Prints 'Hello, world!'.
}
'use strict';

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

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

worker_threads.locks#

Stabilität: 1 - Experimentell

Eine Instanz eines LockManager, der verwendet werden kann, um den Zugriff auf Ressourcen zu koordinieren, die von mehreren Threads innerhalb desselben Prozesses geteilt werden können. Die API spiegelt die Semantik des Browser-LockManager wider.

Klasse: Lock#

Die Lock-Schnittstelle bietet Informationen über eine Sperre, die über locks.request() gewährt wurde.

lock.name#

Der Name der Sperre.

lock.mode#

Der Modus der Sperre. Entweder shared oder exclusive.

Klasse: LockManager#

Die LockManager-Schnittstelle bietet Methoden zum Anfordern und Introspektieren von Sperren. Um eine LockManager-Instanz zu erhalten, verwenden Sie

import { locks } from 'node:worker_threads';
'use strict';

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

Diese Implementierung entspricht der Browser-LockManager-API.

locks.request(name[, options], callback)#
  • name <string>
  • options <Object>
    • mode <string> Entweder 'exclusive' oder 'shared'. Standard: 'exclusive'.
    • ifAvailable <boolean> Wenn true, wird die Anfrage nur gewährt, wenn die Sperre nicht bereits gehalten wird. Wenn sie nicht gewährt werden kann, wird callback mit null anstelle einer Lock-Instanz aufgerufen. Standard: false.
    • steal <boolean> Wenn true, werden alle bestehenden Sperren mit demselben Namen freigegeben und die Anfrage wird sofort gewährt, wobei alle in der Warteschlange stehenden Anfragen überholt werden. Standard: false.
    • signal <AbortSignal>, das verwendet werden kann, um eine ausstehende (aber noch nicht gewährte) Sperranfrage abzubrechen.
  • callback <Function> Wird aufgerufen, sobald die Sperre gewährt wurde (oder sofort mit null, wenn ifAvailable auf true steht und die Sperre nicht verfügbar ist). Die Sperre wird automatisch freigegeben, wenn die Funktion zurückkehrt, oder – falls die Funktion ein Promise zurückgibt – wenn dieses Promise erfüllt ist.
  • Gibt zurück: <Promise> Wird aufgelöst, sobald die Sperre freigegeben wurde.
import { locks } from 'node:worker_threads';

await locks.request('my_resource', async (lock) => {
  // The lock has been acquired.
});
// The lock has been released here.
'use strict';

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

locks.request('my_resource', async (lock) => {
  // The lock has been acquired.
}).then(() => {
  // The lock has been released here.
});
locks.query()#

Wird mit einem LockManagerSnapshot aufgelöst, das die aktuell gehaltenen und ausstehenden Sperren für den aktuellen Prozess beschreibt.

import { locks } from 'node:worker_threads';

const snapshot = await locks.query();
for (const lock of snapshot.held) {
  console.log(`held lock: name ${lock.name}, mode ${lock.mode}`);
}
for (const pending of snapshot.pending) {
  console.log(`pending lock: name ${pending.name}, mode ${pending.mode}`);
}
'use strict';

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

locks.query().then((snapshot) => {
  for (const lock of snapshot.held) {
    console.log(`held lock: name ${lock.name}, mode ${lock.mode}`);
  }
  for (const pending of snapshot.pending) {
    console.log(`pending lock: name ${pending.name}, mode ${pending.mode}`);
  }
});

Klasse: BroadcastChannel extends EventTarget#

Instanzen von BroadcastChannel ermöglichen asynchrone Eins-zu-vielen-Kommunikation mit allen anderen BroadcastChannel-Instanzen, die an denselben Kanalnamen gebunden sind.

import {
  isMainThread,
  BroadcastChannel,
  Worker,
} from '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(new URL(import.meta.url));
} else {
  bc.postMessage('hello from every worker');
  bc.close();
}
'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)#

  • name <any> Der Name des Kanals, zu dem eine Verbindung hergestellt werden soll. Jeder JavaScript-Wert, der mit `${name}` in einen String konvertiert werden kann, ist zulässig.

broadcastChannel.close()#

Schließt die BroadcastChannel-Verbindung.

broadcastChannel.onmessage#

  • Typ: <Function> Wird mit einem einzelnen MessageEvent-Argument aufgerufen, wenn eine Nachricht empfangen wird.

broadcastChannel.onmessageerror#

  • Typ: <Function> Wird aufgerufen, wenn eine empfangene Nachricht nicht deserialisiert werden kann.

broadcastChannel.postMessage(message)#

  • message <any> Jeder klonbare JavaScript-Wert.

broadcastChannel.ref()#

Das Gegenteil von unref(). Der Aufruf von ref() auf einen zuvor unref()ed BroadcastChannel lässt das Programm nicht beenden, wenn es der einzige verbleibende aktive Handle ist (das Standardverhalten). Wenn der Port ref()ed ist, hat ein erneuter Aufruf von ref() keine Auswirkungen.

broadcastChannel.unref()#

Der Aufruf von unref() auf einem BroadcastChannel ermöglicht es dem Thread, sich zu beenden, wenn dies der einzige aktive Handle im Ereignissystem ist. Wenn der BroadcastChannel bereits unref()ed ist, hat ein erneuter Aufruf von unref() keine Auswirkungen.

Klasse: MessageChannel#

Instanzen der worker.MessageChannel-Klasse stellen einen asynchronen, bidirektionalen Kommunikationskanal dar. MessageChannel hat keine eigenen Methoden. new MessageChannel() ergibt ein Objekt mit port1- und port2-Eigenschaften, die auf verknüpfte MessagePort-Instanzen verweisen.

import { MessageChannel } from '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
'use strict';

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

Klasse: MessagePort#

Instanzen der worker.MessagePort-Klasse stellen ein Ende eines asynchronen, bidirektionalen Kommunikationskanals dar. Sie können verwendet werden, um strukturierte Daten, Speicherbereiche und andere MessagePorts zwischen verschiedenen Workers zu übertragen.

Diese Implementierung entspricht Browser-MessagePorts.

Ereignis: 'close'#

Das 'close'-Ereignis wird ausgelöst, sobald eine der beiden Seiten des Kanals getrennt wurde.

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

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

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

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

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

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

Ereignis: 'message'#

  • value <any> Der übertragene Wert

Das 'message'-Ereignis wird für jede eingehende Nachricht ausgelöst und enthält den geklonten Input von port.postMessage().

Listener für dieses Ereignis empfangen einen Klon des value-Parameters, wie er an postMessage() übergeben wurde, und keine weiteren Argumente.

Ereignis: 'messageerror'#

Das 'messageerror'-Ereignis wird ausgelöst, wenn die Deserialisierung einer Nachricht fehlgeschlagen ist.

Derzeit wird dieses Ereignis ausgelöst, wenn ein Fehler bei der Instanziierung des geposteten JS-Objekts auf der Empfängerseite auftritt. Solche Situationen sind selten, können aber beispielsweise vorkommen, wenn bestimmte Node.js-API-Objekte in einem vm.Context empfangen werden (wo Node.js-APIs derzeit nicht verfügbar sind).

port.close()#

Deaktiviert das weitere Senden von Nachrichten auf beiden Seiten der Verbindung. Diese Methode kann aufgerufen werden, wenn über diesen MessagePort keine weitere Kommunikation erfolgt.

Das 'close'-Ereignis wird auf beiden MessagePort-Instanzen ausgelöst, die Teil des Kanals sind.

port.postMessage(value[, transferList])#

Sendet einen JavaScript-Wert an die Empfängerseite dieses Kanals. value wird auf eine Weise übertragen, die mit dem HTML Structured Clone Algorithm kompatibel ist.

Insbesondere die wesentlichen Unterschiede zu JSON sind:

import { MessageChannel } from '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);
'use strict';

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 kann eine Liste von <ArrayBuffer>-, MessagePort- und FileHandle-Objekten sein. Nach der Übertragung sind sie auf der sendenden Seite des Kanals nicht mehr verwendbar (selbst wenn sie nicht in value enthalten sind). Anders als bei Kindprozessen wird das Übertragen von Handles wie Netzwerk-Sockets derzeit nicht unterstützt.

Wenn value <SharedArrayBuffer>-Instanzen enthält, sind diese von jedem Thread aus zugänglich. Sie können nicht in transferList aufgelistet werden.

value kann weiterhin ArrayBuffer-Instanzen enthalten, die nicht in transferList enthalten sind; in diesem Fall wird der zugrunde liegende Speicher kopiert, anstatt verschoben zu werden.

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

port1.on('message', (message) => console.log(message));

const uint8Array = new Uint8Array([ 1, 2, 3, 4 ]);
// This posts a copy of `uint8Array`:
port2.postMessage(uint8Array);
// This does not copy data, but renders `uint8Array` unusable:
port2.postMessage(uint8Array, [ uint8Array.buffer ]);

// The memory for the `sharedUint8Array` is accessible from both the
// original and the copy received by `.on('message')`:
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4));
port2.postMessage(sharedUint8Array);

// This transfers a freshly created message port to the receiver.
// This can be used, for example, to create communication channels between
// multiple `Worker` threads that are children of the same parent thread.
const otherChannel = new MessageChannel();
port2.postMessage({ port: otherChannel.port1 }, [ otherChannel.port1 ]);
'use strict';

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

port1.on('message', (message) => console.log(message));

const uint8Array = new Uint8Array([ 1, 2, 3, 4 ]);
// This posts a copy of `uint8Array`:
port2.postMessage(uint8Array);
// This does not copy data, but renders `uint8Array` unusable:
port2.postMessage(uint8Array, [ uint8Array.buffer ]);

// The memory for the `sharedUint8Array` is accessible from both the
// original and the copy received by `.on('message')`:
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4));
port2.postMessage(sharedUint8Array);

// This transfers a freshly created message port to the receiver.
// This can be used, for example, to create communication channels between
// multiple `Worker` threads that are children of the same parent thread.
const otherChannel = new MessageChannel();
port2.postMessage({ port: otherChannel.port1 }, [ otherChannel.port1 ]);

Das Nachrichtenobjekt wird sofort geklont und kann nach dem Posten geändert werden, ohne Nebenwirkungen zu haben.

Weitere Informationen zu den Serialisierungs- und Deserialisierungsmechanismen hinter dieser API finden Sie in der Serialisierungs-API des node:v8-Moduls.

Überlegungen beim Übertragen von TypedArrays und Buffern#

Alle <TypedArray> | <Buffer>-Instanzen sind Ansichten über einen zugrunde liegenden <ArrayBuffer>. Das heißt, es ist der ArrayBuffer, der tatsächlich die Rohdaten speichert, während die TypedArray- und Buffer-Objekte eine Möglichkeit bieten, die Daten zu betrachten und zu manipulieren. Es ist möglich und üblich, mehrere Ansichten über dieselbe ArrayBuffer-Instanz zu erstellen. Große Sorgfalt ist geboten, wenn eine Transferliste zum Übertragen eines ArrayBuffer verwendet wird, da dies dazu führt, dass alle TypedArray- und Buffer-Instanzen, die denselben ArrayBuffer teilen, unbrauchbar werden.

const ab = new ArrayBuffer(10);

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

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

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

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

Bei Buffer-Instanzen hängt es insbesondere davon ab, wie Instanzen erstellt wurden, ob der zugrunde liegende ArrayBuffer übertragen oder geklont werden kann, was oft nicht zuverlässig bestimmt werden kann.

Ein ArrayBuffer kann mit markAsUntransferable() markiert werden, um anzugeben, dass er immer geklont und niemals übertragen werden soll.

Je nachdem, wie eine Buffer-Instanz erstellt wurde, besitzt sie möglicherweise ihren zugrunde liegenden ArrayBuffer oder nicht. Ein ArrayBuffer darf nicht übertragen werden, es sei denn, es ist bekannt, dass die Buffer-Instanz ihn besitzt. Insbesondere für Buffers, die aus dem internen Buffer-Pool erstellt wurden (unter Verwendung von z. B. Buffer.from() oder Buffer.allocUnsafe()), ist eine Übertragung nicht möglich; sie werden immer geklont, was eine Kopie des gesamten Buffer-Pools sendet. Dieses Verhalten kann mit unbeabsichtigt höherem Speicherverbrauch und möglichen Sicherheitsbedenken verbunden sein.

Siehe Buffer.allocUnsafe() für weitere Details zum Buffer-Pooling.

Die ArrayBuffers für Buffer-Instanzen, die mit Buffer.alloc() oder Buffer.allocUnsafeSlow() erstellt wurden, können immer übertragen werden, aber dies macht alle anderen bestehenden Ansichten dieser ArrayBuffers unbrauchbar.

Überlegungen beim Klonen von Objekten mit Prototypen, Klassen und Accessoren#

Da die Objektklonierung den HTML Structured Clone Algorithm verwendet, werden nicht-aufzählbare Eigenschaften, Eigenschaften-Accessoren und Objektprototypen nicht beibehalten. Insbesondere werden <Buffer>-Objekte auf der Empfängerseite als einfache <Uint8Array>s gelesen, und Instanzen von JavaScript-Klassen werden als einfache JavaScript-Objekte geklont.

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 }

Diese Einschränkung erstreckt sich auf viele eingebaute Objekte, wie das globale URL-Objekt.

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

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

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

// Prints: { }

port.hasRef()#

Wenn wahr, hält das MessagePort-Objekt die Node.js-Ereignisschleife aktiv.

port.ref()#

Das Gegenteil von unref(). Der Aufruf von ref() auf einen zuvor unref()ed Port lässt das Programm nicht beenden, wenn es der einzige verbleibende aktive Handle ist (das Standardverhalten). Wenn der Port ref()ed ist, hat ein erneuter Aufruf von ref() keine Auswirkungen.

Wenn Listener mit .on('message') hinzugefügt oder entfernt werden, wird der Port automatisch ref()ed bzw. unref()ed, je nachdem, ob Listener für das Ereignis existieren.

port.start()#

Beginnt den Empfang von Nachrichten auf diesem MessagePort. Bei Verwendung dieses Ports als Event-Emitter wird dies automatisch aufgerufen, sobald 'message'-Listener hinzugefügt wurden.

Diese Methode existiert zur Parität mit der Web-MessagePort-API. In Node.js ist sie nur nützlich, um Nachrichten zu ignorieren, wenn kein Event-Listener vorhanden ist. Node.js unterscheidet sich auch in der Handhabung von .onmessage. Das Setzen ruft automatisch .start() auf, aber das Aufheben lässt Nachrichten in der Warteschlange, bis ein neuer Handler gesetzt wird oder der Port verworfen wird.

port.unref()#

Der Aufruf von unref() auf einem Port ermöglicht es dem Thread, sich zu beenden, wenn dies der einzige aktive Handle im Ereignissystem ist. Wenn der Port bereits unref()ed ist, hat ein erneuter Aufruf von unref() keine Auswirkungen.

Wenn Listener mit .on('message') hinzugefügt oder entfernt werden, wird der Port automatisch ref()ed bzw. unref()ed, je nachdem, ob Listener für das Ereignis existieren.

Klasse: Worker#

Die Worker-Klasse repräsentiert einen unabhängigen JavaScript-Ausführungsthread. Die meisten Node.js-APIs sind darin verfügbar.

Bemerkenswerte Unterschiede innerhalb einer Worker-Umgebung sind:

Das Erstellen von Worker-Instanzen innerhalb anderer Workers ist möglich.

Wie bei Web-Workern und dem node:cluster-Modul kann eine bidirektionale Kommunikation durch Nachrichtenübertragung zwischen Threads erreicht werden. Intern hat ein Worker ein eingebautes Paar von MessagePorts, die bereits miteinander verknüpft sind, wenn der Worker erstellt wird. Während das MessagePort-Objekt auf der übergeordneten Seite nicht direkt verfügbar ist, sind seine Funktionen über worker.postMessage() und das Ereignis worker.on('message') auf dem Worker-Objekt für den übergeordneten Thread verfügbar.

Um benutzerdefinierte Nachrichtenkanäle zu erstellen (was gegenüber der Verwendung des standardmäßigen globalen Kanals empfohlen wird, da es die Trennung von Belangen erleichtert), können Benutzer ein MessageChannel-Objekt auf jedem Thread erstellen und einen der MessagePorts auf diesem MessageChannel über einen bereits bestehenden Kanal, wie den globalen, an den anderen Thread übergeben.

Siehe port.postMessage() für weitere Informationen darüber, wie Nachrichten übertragen werden und welche Art von JavaScript-Werten erfolgreich durch die Thread-Barriere transportiert werden können.

import assert from 'node:assert';
import {
  Worker, MessageChannel, MessagePort, isMainThread, parentPort,
} from 'node:worker_threads';
if (isMainThread) {
  const worker = new Worker(new URL(import.meta.url));
  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();
  });
}
'use strict';

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

  • filename <string> | <URL> Der Pfad zum Hauptskript oder Modul des Workers. Muss entweder ein absoluter Pfad oder ein relativer Pfad (d. h. relativ zum aktuellen Arbeitsverzeichnis) sein, der mit ./ oder ../ beginnt, oder ein WHATWG-URL-Objekt, das das file:- oder data:-Protokoll verwendet. Bei Verwendung einer data:-URL werden die Daten basierend auf dem MIME-Typ unter Verwendung des ECMAScript-Modul-Loaders interpretiert. Wenn options.eval true ist, ist dies ein String, der JavaScript-Code enthält, anstatt eines Pfads.
  • options <Object>
    • argv <any[]> Liste von Argumenten, die als String formatiert und an process.argv im Worker angehängt werden. Dies ist größtenteils ähnlich wie workerData, aber die Werte sind auf dem globalen process.argv verfügbar, als ob sie als CLI-Optionen an das Skript übergeben worden wären.
    • env <Object> Wenn gesetzt, gibt es den Anfangswert von process.env innerhalb des Worker-Threads an. Als spezieller Wert kann worker.SHARE_ENV verwendet werden, um anzugeben, dass der übergeordnete Thread und der untergeordnete Thread ihre Umgebungsvariablen teilen sollen; in diesem Fall wirken sich Änderungen am process.env-Objekt eines Threads auch auf den anderen Thread aus. Standard: process.env.
    • eval <boolean> Wenn true und das erste Argument ein string ist, interpretieren Sie das erste Argument des Konstruktors als Skript, das ausgeführt wird, sobald der Worker online ist.
    • execArgv <string[]> Liste von Node-CLI-Optionen, die an den Worker übergeben werden. V8-Optionen (wie --max-old-space-size) und Optionen, die den Prozess beeinflussen (wie --title), werden nicht unterstützt. Wenn gesetzt, wird dies als process.execArgv innerhalb des Workers bereitgestellt. Standardmäßig werden Optionen vom übergeordneten Thread geerbt.
    • stdin <boolean> Wenn dies auf true gesetzt ist, stellt worker.stdin einen beschreibbaren Stream bereit, dessen Inhalt innerhalb des Workers als process.stdin erscheint. Standardmäßig werden keine Daten bereitgestellt.
    • stdout <boolean> Wenn dies auf true gesetzt ist, wird worker.stdout nicht automatisch über process.stdout im übergeordneten Thread geleitet.
    • stderr <boolean> Wenn dies auf true gesetzt ist, wird worker.stderr nicht automatisch über process.stderr im übergeordneten Thread geleitet.
    • workerData <any> Jeder JavaScript-Wert, der geklont und als require('node:worker_threads').workerData verfügbar gemacht wird. Das Klonen erfolgt wie im HTML Structured Clone Algorithm beschrieben, und ein Fehler wird ausgelöst, wenn das Objekt nicht geklont werden kann (z. B. weil es functions enthält).
    • trackUnmanagedFds <boolean> Wenn dies auf true gesetzt ist, verfolgt der Worker rohe Dateideskriptoren, die über fs.open() und fs.close() verwaltet werden, und schließt sie, wenn der Worker beendet wird, ähnlich wie bei anderen Ressourcen wie Netzwerk-Sockets oder Dateideskriptoren, die über die FileHandle-API verwaltet werden. Diese Option wird automatisch von allen verschachtelten Workers geerbt. Standard: true.
    • transferList <Object[]> Wenn ein oder mehrere MessagePort-ähnliche Objekte in workerData übergeben werden, ist eine transferList für diese Elemente erforderlich, andernfalls wird ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST ausgelöst. Siehe port.postMessage() für weitere Informationen.
    • resourceLimits <Object> Ein optionaler Satz von Ressourcenbeschränkungen für die neue JS-Engine-Instanz. Das Erreichen dieser Limits führt zur Beendigung der Worker-Instanz. Diese Limits betreffen nur die JS-Engine und keine externen Daten, einschließlich keiner ArrayBuffers. Selbst wenn diese Limits gesetzt sind, kann der Prozess dennoch abgebrochen werden, wenn eine globale Out-of-Memory-Situation auftritt.
    • maxOldGenerationSizeMb <number> Die maximale Größe des Haupt-Heaps in MB. Wenn das Befehlszeilenargument --max-old-space-size gesetzt ist, überschreibt es diese Einstellung.
    • maxYoungGenerationSizeMb <number> Die maximale Größe eines Heap-Bereichs für kürzlich erstellte Objekte. Wenn das Befehlszeilenargument --max-semi-space-size gesetzt ist, überschreibt es diese Einstellung.
    • codeRangeSizeMb <number> Die Größe eines vorab zugewiesenen Speicherbereichs, der für generierten Code verwendet wird.
    • stackSizeMb <number> Die standardmäßige maximale Stapelgröße für den Thread. Kleine Werte können zu unbrauchbaren Worker-Instanzen führen. Standard: 4.
  • name <string> Ein optionaler name, der im Thread-Namen und im Worker-Titel zu Debugging-/Identifikationszwecken ersetzt wird, wodurch der endgültige Titel zu [worker ${id}] ${name} wird. Dieser Parameter hat je nach Betriebssystem eine maximal zulässige Größe. Wenn der angegebene Name das Limit überschreitet, wird er abgeschnitten.
  • Maximale Größen:
  • Windows: 32.767 Zeichen
  • macOS: 64 Zeichen
  • Linux: 16 Zeichen
  • NetBSD: begrenzt auf PTHREAD_MAX_NAMELEN_NP
  • FreeBSD und OpenBSD: begrenzt auf MAXCOMLEN Standard: 'WorkerThread'.

Ereignis: 'error'#

Das 'error'-Ereignis wird ausgelöst, wenn der Worker-Thread eine nicht abgefangene Ausnahme auslöst. In diesem Fall wird der Worker beendet.

Ereignis: 'exit'#

Das 'exit'-Ereignis wird ausgelöst, sobald der Worker gestoppt wurde. Wenn der Worker durch Aufrufen von process.exit() beendet wurde, ist der exitCode-Parameter der übergebene Exit-Code. Wenn der Worker beendet wurde, ist der exitCode-Parameter 1.

Dies ist das letzte Ereignis, das von einer Worker-Instanz ausgegeben wird.

Ereignis: 'message'#

  • value <any> Der übertragene Wert

Das 'message'-Ereignis wird ausgelöst, wenn der Worker-Thread require('node:worker_threads').parentPort.postMessage() aufgerufen hat. Siehe das port.on('message')-Ereignis für weitere Details.

Alle vom Worker-Thread gesendeten Nachrichten werden ausgegeben, bevor das 'exit'-Ereignis auf dem Worker-Objekt ausgegeben wird.

Ereignis: 'messageerror'#

Das 'messageerror'-Ereignis wird ausgelöst, wenn die Deserialisierung einer Nachricht fehlgeschlagen ist.

Ereignis: 'online'#

Das 'online'-Ereignis wird ausgelöst, wenn der Worker-Thread mit der Ausführung von JavaScript-Code begonnen hat.

worker.cpuUsage([prev])#

Diese Methode gibt ein Promise zurück, das zu einem Objekt aufgelöst wird, das mit process.threadCpuUsage() identisch ist, oder mit einem ERR_WORKER_NOT_RUNNING-Fehler abgelehnt wird, wenn der Worker nicht mehr läuft. Diese Methode ermöglicht es, die Statistiken von außerhalb des tatsächlichen Threads zu beobachten.

worker.getHeapSnapshot([options])#

  • options <Object>
    • exposeInternals <boolean> Wenn wahr, werden Interna im Heap-Snapshot offengelegt. Standard: false.
    • exposeNumericValues <boolean> Wenn wahr, werden numerische Werte in künstlichen Feldern offengelegt. Standard: false.
  • Gibt zurück: <Promise> Ein Promise für einen lesbaren Stream, der einen V8-Heap-Snapshot enthält.

Gibt einen lesbaren Stream für einen V8-Snapshot des aktuellen Zustands des Workers zurück. Siehe v8.getHeapSnapshot() für weitere Details.

Wenn der Worker-Thread nicht mehr läuft, was vor dem Auslösen des 'exit'-Ereignisses passieren kann, wird das zurückgegebene Promise sofort mit einem ERR_WORKER_NOT_RUNNING-Fehler abgelehnt.

worker.getHeapStatistics()#

Diese Methode gibt ein Promise zurück, das zu einem Objekt aufgelöst wird, das mit v8.getHeapStatistics() identisch ist, oder mit einem ERR_WORKER_NOT_RUNNING-Fehler abgelehnt wird, wenn der Worker nicht mehr läuft. Diese Methode ermöglicht es, die Statistiken von außerhalb des tatsächlichen Threads zu beobachten.

worker.performance#

Ein Objekt, das verwendet werden kann, um Leistungsinformationen von einer Worker-Instanz abzufragen.

performance.eventLoopUtilization([utilization1[, utilization2]])#
  • utilization1 <Object> Das Ergebnis eines vorherigen Aufrufs von eventLoopUtilization().
  • utilization2 <Object> Das Ergebnis eines vorherigen Aufrufs von eventLoopUtilization() vor utilization1.
  • Rückgabewert: <Object>

Der gleiche Aufruf wie perf_hooks eventLoopUtilization(), außer dass die Werte der Worker-Instanz zurückgegeben werden.

Ein Unterschied ist, dass das Bootstrapping innerhalb eines Workers im Gegensatz zum Haupt-Thread innerhalb der Ereignisschleife erfolgt. Die Auslastung der Ereignisschleife ist also sofort verfügbar, sobald das Skript des Workers die Ausführung beginnt.

Eine idle-Zeit, die nicht zunimmt, bedeutet nicht, dass der Worker im Bootstrapping feststeckt. Die folgenden Beispiele zeigen, wie die gesamte Lebensdauer des Workers niemals idle-Zeit ansammelt, aber dennoch Nachrichten verarbeiten kann.

import { Worker, isMainThread, parentPort } from 'node:worker_threads';

if (isMainThread) {
  const worker = new Worker(new URL(import.meta.url));
  setInterval(() => {
    worker.postMessage('hi');
    console.log(worker.performance.eventLoopUtilization());
  }, 100).unref();
} else {
  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);
}
'use strict';

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();
} else {
  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);
}

Die Ereignisschleifenauslastung eines Workers ist erst verfügbar, nachdem das 'online'-Ereignis ausgegeben wurde. Wenn dies zuvor oder nach dem 'exit'-Ereignis aufgerufen wird, haben alle Eigenschaften den Wert 0.

worker.postMessage(value[, transferList])#

Sendet eine Nachricht an den Worker, die über require('node:worker_threads').parentPort.on('message') empfangen wird. Siehe port.postMessage() für weitere Details.

worker.ref()#

Das Gegenteil von unref(). Der Aufruf von ref() auf einen zuvor unref()ed Worker lässt das Programm nicht beenden, wenn es der einzige verbleibende aktive Handle ist (das Standardverhalten). Wenn der Worker ref()ed ist, hat ein erneuter Aufruf von ref() keine Auswirkungen.

worker.resourceLimits#

Bietet die Menge der Ressourcenbeschränkungen der JS-Engine für diesen Worker-Thread. Wenn die Option resourceLimits an den Worker-Konstruktor übergeben wurde, entspricht dies dessen Werten.

Wenn der Worker gestoppt hat, ist der Rückgabewert ein leeres Objekt.

worker.startCpuProfile()#

Das Starten eines CPU-Profils gibt dann ein Promise zurück, das mit einem Fehler oder einem CPUProfileHandle-Objekt erfüllt wird. Diese API unterstützt die await using-Syntax.

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

const worker = new Worker(`
  const { parentPort } = require('worker_threads');
  parentPort.on('message', () => {});
  `, { eval: true });

worker.on('online', async () => {
  const handle = await worker.startCpuProfile();
  const profile = await handle.stop();
  console.log(profile);
  worker.terminate();
});

await using-Beispiel.

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

const w = new Worker(`
  const { parentPort } = require('node:worker_threads');
  parentPort.on('message', () => {});
  `, { eval: true });

w.on('online', async () => {
  // Stop profile automatically when return and profile will be discarded
  await using handle = await w.startCpuProfile();
});

worker.startHeapProfile()#

Das Starten eines Heap-Profils gibt dann ein Promise zurück, das mit einem Fehler oder einem HeapProfileHandle-Objekt erfüllt wird. Diese API unterstützt die await using-Syntax.

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

const worker = new Worker(`
  const { parentPort } = require('worker_threads');
  parentPort.on('message', () => {});
  `, { eval: true });

worker.on('online', async () => {
  const handle = await worker.startHeapProfile();
  const profile = await handle.stop();
  console.log(profile);
  worker.terminate();
});

await using-Beispiel.

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

const w = new Worker(`
  const { parentPort } = require('node:worker_threads');
  parentPort.on('message', () => {});
  `, { eval: true });

w.on('online', async () => {
  // Stop profile automatically when return and profile will be discarded
  await using handle = await w.startHeapProfile();
});

worker.stderr#

Dies ist ein lesbarer Stream, der Daten enthält, die innerhalb des Worker-Threads in process.stderr geschrieben wurden. Wenn stderr: true nicht an den Worker-Konstruktor übergeben wurde, werden die Daten an den process.stderr-Stream des übergeordneten Threads geleitet.

worker.stdin#

Wenn stdin: true an den Worker-Konstruktor übergeben wurde, ist dies ein beschreibbarer Stream. Die in diesen Stream geschriebenen Daten werden im Worker-Thread als process.stdin verfügbar gemacht.

worker.stdout#

Dies ist ein lesbarer Stream, der Daten enthält, die innerhalb des Worker-Threads in process.stdout geschrieben wurden. Wenn stdout: true nicht an den Worker-Konstruktor übergeben wurde, werden die Daten an den process.stdout-Stream des übergeordneten Threads geleitet.

worker.terminate()#

Stoppt die gesamte JavaScript-Ausführung im Worker-Thread so schnell wie möglich. Gibt ein Promise für den Exit-Code zurück, das erfüllt wird, wenn das 'exit'-Ereignis ausgegeben wird.

worker.threadId#

Eine Ganzzahl-Kennung für den referenzierten Thread. Innerhalb des Worker-Threads ist sie als require('node:worker_threads').threadId verfügbar. Dieser Wert ist für jede Worker-Instanz innerhalb eines einzelnen Prozesses eindeutig.

worker.threadName#

Ein String-Bezeichner für den referenzierten Thread oder null, wenn der Thread nicht läuft. Innerhalb des Worker-Threads ist sie als require('node:worker_threads').threadName verfügbar.

worker.unref()#

Der Aufruf von unref() auf einem Worker ermöglicht es dem Thread, sich zu beenden, wenn dies der einzige aktive Handle im Ereignissystem ist. Wenn der Worker bereits unref()ed ist, hat ein erneuter Aufruf von unref() keine Auswirkungen.

worker[Symbol.asyncDispose]()#

Ruft worker.terminate() auf, wenn der Dispose-Scope verlassen wird.

async function example() {
  await using worker = new Worker('for (;;) {}', { eval: true });
  // Worker is automatically terminate when the scope is exited.
}

Hinweise#

Synchrones Blockieren von stdio#

Worker nutzen Nachrichtenübertragung via <MessagePort>, um Interaktionen mit stdio zu implementieren. Dies bedeutet, dass stdio-Ausgaben, die von einem Worker stammen, durch synchronen Code auf der Empfängerseite blockiert werden können, der die Node.js-Ereignisschleife blockiert.

import {
  Worker,
  isMainThread,
} from 'node:worker_threads';

if (isMainThread) {
  new Worker(new URL(import.meta.url));
  for (let n = 0; n < 1e10; n++) {
    // Looping to simulate work.
  }
} else {
  // This output will be blocked by the for loop in the main thread.
  console.log('foo');
}
'use strict';

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

if (isMainThread) {
  new Worker(__filename);
  for (let n = 0; n < 1e10; n++) {
    // Looping to simulate work.
  }
} else {
  // This output will be blocked by the for loop in the main thread.
  console.log('foo');
}

Starten von Worker-Threads aus Preload-Skripten#

Seien Sie vorsichtig beim Starten von Worker-Threads aus Preload-Skripten (Skripte, die mit dem -r-Befehlszeilenflag geladen und ausgeführt werden). Sofern die Option execArgv nicht explizit gesetzt ist, erben neue Worker-Threads automatisch die Befehlszeilenflags des laufenden Prozesses und laden dieselben Preload-Skripte wie der Haupt-Thread. Wenn das Preload-Skript bedingungslos einen Worker-Thread startet, startet jeder erzeugte Thread einen weiteren, bis die Anwendung abstürzt.