Node.js v20.11.0 文档


工作线程#

Worker threads

稳定性: 2 - 稳定的

Stability: 2 - Stable

源代码: lib/worker_threads.js

node:worker_threads 模块允许使用并行执行 JavaScript 的线程。要访问它:

The node:worker_threads module enables the use of threads that execute JavaScript in parallel. To access it:

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

工作线程对于执行 CPU 密集型的 JavaScript 操作很有用。它们对 I/O 密集型的工作帮助不大。Node.js 内置的异步 I/O 操作比工作线程更高效。

Workers (threads) are useful for performing CPU-intensive JavaScript operations. They do not help much with I/O-intensive work. The Node.js built-in asynchronous I/O operations are more efficient than Workers can be.

child_processcluster 不同,worker_threads 可以共享内存。它们通过传输 ArrayBuffer 实例或共享 SharedArrayBuffer 实例来实现。

Unlike child_process or cluster, worker_threads can share memory. They do so by transferring ArrayBuffer instances or sharing SharedArrayBuffer instances.

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

上面的示例为每个 parseJSAsync() 调用衍生工作线程。在实践中,为这些类型的任务使用工作线程池。否则,创建工作线程的开销可能会超过其收益。

The above example spawns a Worker thread for each parseJSAsync() call. In practice, use a pool of Workers for these kinds of tasks. Otherwise, the overhead of creating Workers would likely exceed their benefit.

当实现工作线程池时,使用 AsyncResource API 通知诊断工具(例如提供异步的堆栈跟踪)有关任务与其结果之间的相关性。有关示例实现,请参阅 async_hooks 文档中的 “将 AsyncResource 用于 Worker 线程池”

When implementing a worker pool, use the AsyncResource API to inform diagnostic tools (e.g. to provide asynchronous stack traces) about the correlation between tasks and their outcomes. See "Using AsyncResource for a Worker thread pool" in the async_hooks documentation for an example implementation.

默认情况下,工作线程继承非进程特定的选项。参考 Worker constructor options 了解如何自定义工作线程选项,特别是 argvexecArgv 选项。

Worker threads inherit non-process-specific options by default. Refer to Worker constructor options to know how to customize worker thread options, specifically argv and execArgv options.

worker.getEnvironmentData(key)#

  • key <any> 任何可以用作 <Map> 键的任意、可克隆的 JavaScript 值。

    key <any> Any arbitrary, cloneable JavaScript value that can be used as a <Map> key.

  • 返回:<any>

    Returns: <any>

在工作线程中,worker.getEnvironmentData() 返回传给衍生线程的 worker.setEnvironmentData() 的数据的克隆。每个新的 Worker 都会自动接收到自己的环境数据的副本。

Within a worker thread, worker.getEnvironmentData() returns a clone of data passed to the spawning thread's worker.setEnvironmentData(). Every new Worker receives its own copy of the environment data automatically.

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.isMainThread#

如果此代码不在 Worker 线程内运行,则为 true

Is true if this code is not running inside of a Worker thread.

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.markAsUntransferable(object)#

将对象标记为不可传输。如果 object 出现在 port.postMessage() 调用的传输列表中,则忽略它。

Mark an object as not transferable. If object occurs in the transfer list of a port.postMessage() call, it is ignored.

特别是,这对于可以克隆而不是传输的对象,以及被发送方的其他对象使用的对象来说是有意义的。例如,Node.js 将 ArrayBuffer 用于 Buffer 标记。

In particular, this makes sense for objects that can be cloned, rather than transferred, and which are used by other objects on the sending side. For example, Node.js marks the ArrayBuffers it uses for its Buffer pool with this.

此操作无法撤消。

This operation cannot be undone.

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();
port1.postMessage(typedArray1, [ typedArray1.buffer ]);

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

浏览器中没有与此 API 等效的 API。

There is no equivalent to this API in browsers.

worker.moveMessagePortToContext(port, contextifiedSandbox)#

MessagePort 传输到不同的 vm 上下文原始的 port 对象变得不可用,返回的 MessagePort 实例取而代之。

Transfer a MessagePort to a different vm Context. The original port object is rendered unusable, and the returned MessagePort instance takes its place.

返回的 MessagePort 是目标上下文中的对象,并且继承自其全局的 Object 类。传给 port.onmessage() 监听器的对象也在目标上下文中创建并且从其全局的 Object 类继承。

The returned MessagePort is an object in the target context and inherits from its global Object class. Objects passed to the port.onmessage() listener are also created in the target context and inherit from its global Object class.

但是,创建的 MessagePort 不再继承 EventTarget,只有 port.onmessage() 可以使用它来接收事件。

However, the created MessagePort no longer inherits from EventTarget, and only port.onmessage() can be used to receive events using it.

worker.parentPort#

如果此线程是 Worker,则这是允许与父线程通信的 MessagePort。使用 parentPort.postMessage() 发送的消息在使用 worker.on('message') 的父线程中可用,使用 worker.postMessage() 从父线程发送的消息在使用 parentPort.on('message') 的该线程中可用。

If this thread is a Worker, this is a MessagePort allowing communication with the parent thread. Messages sent using parentPort.postMessage() are available in the parent thread using worker.on('message'), and messages sent from the parent thread using worker.postMessage() are available in this thread using parentPort.on('message').

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

if (isMainThread) {
  const worker = new Worker(__filename);
  worker.once('message', (message) => {
    console.log(message);  // 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.receiveMessageOnPort(port)#

从给定的 MessagePort 接收消息。如果没有消息可用,则返回 undefined,否则返回具有单个 message 属性的对象,该对象包含消息负载,对应于 MessagePort 队列中最旧的消息。

Receive a single message from a given MessagePort. If no message is available, undefined is returned, otherwise an object with a single message property that contains the message payload, corresponding to the oldest message in the MessagePort's queue.

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 

当使用此函数时,不会触发 'message' 事件,也不会调用 onmessage 监听器。

When this function is used, no 'message' event is emitted and the onmessage listener is not invoked.

worker.resourceLimits#

在这个工作线程中提供了一组 JS 引擎资源约束。如果将 resourceLimits 选项传给 Worker 构造函数,则这与其值匹配。

Provides the set of JS engine resource constraints inside this Worker thread. If the resourceLimits option was passed to the Worker constructor, this matches its values.

如果在主线程中使用此,则其值为空对象。

If this is used in the main thread, its value is an empty object.

worker.SHARE_ENV#

可以作为 Worker 构造函数的 env 选项传入的特殊值,表示当前线程和工作线程应该共享对同一组环境变量的读写访问。

A special value that can be passed as the env option of the Worker constructor, to indicate that the current thread and the Worker thread should share read and write access to the same set of environment variables.

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

worker.setEnvironmentData(key[, value])#

  • key <any> 任何可以用作 <Map> 键的任意、可克隆的 JavaScript 值。

    key <any> Any arbitrary, cloneable JavaScript value that can be used as a <Map> key.

  • value <any> 任何任意的、可克隆的 JavaScript 值都将被克隆并自动传给所有新的 Worker 实例。如果 value 作为 undefined 传入,则 key 之前设置的任何值都将被删除。

    value <any> Any arbitrary, cloneable JavaScript value that will be cloned and passed automatically to all new Worker instances. If value is passed as undefined, any previously set value for the key will be deleted.

worker.setEnvironmentData() API 设置当前线程中 worker.getEnvironmentData() 的内容以及从当前上下文产生的所有新 Worker 实例。

The worker.setEnvironmentData() API sets the content of worker.getEnvironmentData() in the current thread and all new Worker instances spawned from the current context.

worker.threadId#

当前线程的整数标识符。在对应的工作线程对象上(如果有的话),可以作为 worker.threadId 使用。此值对于单个进程中的每个 Worker 实例都是唯一的。

An integer identifier for the current thread. On the corresponding worker object (if there is any), it is available as worker.threadId. This value is unique for each Worker instance inside a single process.

worker.workerData#

任意的 JavaScript 值,其中包含传给该线程的 Worker 构造函数的数据的克隆。

An arbitrary JavaScript value that contains a clone of the data passed to this thread's Worker constructor.

根据 HTML 结构化克隆算法,数据被克隆,就像使用 postMessage() 一样。

The data is cloned as if using postMessage(), according to the HTML structured clone algorithm.

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!'.
} 

类:BroadcastChannel extends EventTarget#

Class: BroadcastChannel extends EventTarget

BroadcastChannel 的实例允许与绑定到相同通道名称的所有其他 BroadcastChannel 实例进行异步的一对多通信。

Instances of BroadcastChannel allow asynchronous one-to-many communication with all other BroadcastChannel instances bound to the same channel name.

'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> 要连接的通道名称。任何可以使用 ${name} 转换为字符串的 JavaScript 值都是允许的。

    name <any> The name of the channel to connect to. Any JavaScript value that can be converted to a string using `${name}` is permitted.

broadcastChannel.close()#

关闭 BroadcastChannel 连接。

Closes the BroadcastChannel connection.

broadcastChannel.onmessage#

  • 类型:<Function> 当接收到消息时,使用单个 MessageEvent 参数调用。

    Type: <Function> Invoked with a single MessageEvent argument when a message is received.

broadcastChannel.onmessageerror#

  • 类型:<Function> 使用接收到的消息调用不能反序列化。

    Type: <Function> Invoked with a received message cannot be deserialized.

broadcastChannel.postMessage(message)#

  • message <any> 任何可克隆的 JavaScript 值。

    message <any> Any cloneable JavaScript value.

broadcastChannel.ref()#

unref() 的相反。如果它是唯一剩下的活动句柄(默认行为),则在先前 unref() BroadcastChannel 上调用 ref() 不会让程序退出。如果端口是 ref() 的,则再次调用 ref() 没有效果。

Opposite of unref(). Calling ref() on a previously unref()ed BroadcastChannel does not let the program exit if it's the only active handle left (the default behavior). If the port is ref()ed, calling ref() again has no effect.

broadcastChannel.unref()#

如果这是事件系统中唯一的活动句柄,则在广播通道上调用 unref() 允许线程退出。如果广播通道已经 unref(),则再次调用 unref() 无效。

Calling unref() on a BroadcastChannel allows the thread to exit if this is the only active handle in the event system. If the BroadcastChannel is already unref()ed calling unref() again has no effect.

类:MessageChannel#

Class: MessageChannel

worker.MessageChannel 类的实例代表异步的双向通信通道。MessageChannel 没有自己的方法。new MessageChannel() 产生具有 port1port2 属性的对象,其引用链接的 MessagePort 实例。

Instances of the worker.MessageChannel class represent an asynchronous, two-way communications channel. The MessageChannel has no methods of its own. new MessageChannel() yields an object with port1 and port2 properties, which refer to linked MessagePort instances.

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 

类:MessagePort#

Class: MessagePort

worker.MessagePort 类的实例代表异步双向通信通道的一端。它可以用来在不同的 Worker 之间传输结构化数据、内存区域和其他 MessagePort

Instances of the worker.MessagePort class represent one end of an asynchronous, two-way communications channel. It can be used to transfer structured data, memory regions and other MessagePorts between different Workers.

此实现匹配 浏览器 MessagePort

This implementation matches browser MessagePorts.

事件:'close'#

Event: 'close'

一旦通道的任一侧断开连接,则会触发 'close' 事件。

The 'close' event is emitted once either side of the channel has been disconnected.

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

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

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

事件:'message'#

Event: 'message'

  • value <any> 传输值

    value <any> The transmitted value

为任何传入消息触发 'message' 事件,其中包含 port.postMessage() 的克隆输入。

The 'message' event is emitted for any incoming message, containing the cloned input of port.postMessage().

此事件的监听器接收传给 postMessage()value 参数的副本,没有其他参数。

Listeners on this event receive a clone of the value parameter as passed to postMessage() and no further arguments.

事件:'messageerror'#

Event: 'messageerror'

当反序列化消息失败时,则会触发 'messageerror' 事件。

The 'messageerror' event is emitted when deserializing a message failed.

目前,当在接收端实例化已发布的 JS 对象时发生错误时,则会触发此事件。这种情况很少见,但可能会发生,例如,当某些 Node.js API 对象在 vm.Context 中接收到时(Node.js API 当前不可用)。

Currently, this event is emitted when there is an error occurring while instantiating the posted JS object on the receiving end. Such situations are rare, but can happen, for instance, when certain Node.js API objects are received in a vm.Context (where Node.js APIs are currently unavailable).

port.close()#

禁止在连接的任一端进一步发送消息。当此 MessagePort 上不会发生进一步的通信时,可以调用此方法。

Disables further sending of messages on either side of the connection. This method can be called when no further communication will happen over this MessagePort.

'close' 事件 在作为通道一部分的两个 MessagePort 实例上触发。

The 'close' event is emitted on both MessagePort instances that are part of the channel.

port.postMessage(value[, transferList])#

向该通道的接收端发送 JavaScript 值。value 以与 HTML 结构化克隆算法 兼容的方式传输。

Sends a JavaScript value to the receiving side of this channel. value is transferred in a way which is compatible with the HTML structured clone algorithm.

特别是与 JSON 的显着区别是:

In particular, the significant differences to JSON are:

  • value 可能包含循环引用。

    value may contain circular references.

  • value 可能包含内置 JS 类型的实例,例如 RegExpBigIntMapSet 等。

    value may contain instances of builtin JS types such as RegExps, BigInts, Maps, Sets, etc.

  • value 可能包含类型化数组,同时使用 ArrayBufferSharedArrayBuffer

    value may contain typed arrays, both using ArrayBuffers and SharedArrayBuffers.

  • value 可能包含 WebAssembly.Module 实例。

    value may contain WebAssembly.Module instances.

  • value 可能不包含原生 (C++ 支持) 对象,除了:

    value may not contain native (C++-backed) objects other than:

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 可能是 ArrayBufferMessagePortFileHandle 对象的列表。传输后,它们在通道的发送端不再可用(即使它们不包含在 value 中)。与 子进程 不同,目前不支持传输句柄,例如网络套接字。

transferList may be a list of ArrayBuffer, MessagePort, and FileHandle objects. After transferring, they are not usable on the sending side of the channel anymore (even if they are not contained in value). Unlike with child processes, transferring handles such as network sockets is currently not supported.

如果 value 包含 SharedArrayBuffer 实例,则可以从任一线程访问它们。它们不能在 transferList 中列出。

If value contains SharedArrayBuffer instances, those are accessible from either thread. They cannot be listed in transferList.

value 可能仍然包含不在 transferList 中的 ArrayBuffer 个实例;在这种情况下,底层内存被复制而不是移动。

value may still contain ArrayBuffer instances that are not in transferList; in that case, the underlying memory is copied rather than moved.

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

消息对象立即克隆,发布后可修改,无副作用。

The message object is cloned immediately, and can be modified after posting without having side effects.

有关此 API 背后的序列化和反序列化机制的更多信息,请参阅 node:v8 模块的序列化 API

For more information on the serialization and deserialization mechanisms behind this API, see the serialization API of the node:v8 module.

传输 TypedArray 和缓冲区时的注意事项#

Considerations when transferring TypedArrays and Buffers

所有 TypedArrayBuffer 实例都是对底层 ArrayBuffer 的视图。也就是说,实际存储原始数据的是 ArrayBuffer,而 TypedArrayBuffer 对象提供了查看和操作数据的方式。在同一个 ArrayBuffer 实例上创建多个视图是可能且常见的。使用传输列表传输 ArrayBuffer 时必须非常小心,因为这样做会导致共享同一个 ArrayBuffer 的所有 TypedArrayBuffer 实例变得不可用。

All TypedArray and Buffer instances are views over an underlying ArrayBuffer. That is, it is the ArrayBuffer that actually stores the raw data while the TypedArray and Buffer objects provide a way of viewing and manipulating the data. It is possible and common for multiple views to be created over the same ArrayBuffer instance. Great care must be taken when using a transfer list to transfer an ArrayBuffer as doing so causes all TypedArray and Buffer instances that share that same ArrayBuffer to become unusable.

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 

对于 Buffer 实例,具体来说,底层 ArrayBuffer 是否可以被传输或克隆完全取决于实例是如何创建的,这通常无法可靠地确定。

For Buffer instances, specifically, whether the underlying ArrayBuffer can be transferred or cloned depends entirely on how instances were created, which often cannot be reliably determined.

ArrayBuffer 可以用 markAsUntransferable() 标记来表示它应该总是被克隆并且永远不会被传输。

An ArrayBuffer can be marked with markAsUntransferable() to indicate that it should always be cloned and never transferred.

根据 Buffer 实例的创建方式,它可能拥有也可能不拥有其底层 ArrayBuffer。除非知道 Buffer 实例拥有它,否则不得传输 ArrayBuffer。特别是,对于从内部 Buffer 池(例如使用 Buffer.from()Buffer.allocUnsafe())创建的 Buffer,传输它们是不可能的,它们总是被克隆,这会发送整个 Buffer 池的副本。此行为可能会带来意想不到的更高内存使用率和可能的安全问题。

Depending on how a Buffer instance was created, it may or may not own its underlying ArrayBuffer. An ArrayBuffer must not be transferred unless it is known that the Buffer instance owns it. In particular, for Buffers created from the internal Buffer pool (using, for instance Buffer.from() or Buffer.allocUnsafe()), transferring them is not possible and they are always cloned, which sends a copy of the entire Buffer pool. This behavior may come with unintended higher memory usage and possible security concerns.

有关 Buffer 池化的更多详细信息,请参阅 Buffer.allocUnsafe()

See Buffer.allocUnsafe() for more details on Buffer pooling.

使用 Buffer.alloc()Buffer.allocUnsafeSlow() 创建的 Buffer 实例的 ArrayBuffer 始终可以传输,但这样做会使这些 ArrayBuffer 的所有其他现有视图不可用。

The ArrayBuffers for Buffer instances created using Buffer.alloc() or Buffer.allocUnsafeSlow() can always be transferred but doing so renders all other existing views of those ArrayBuffers unusable.

使用原型、类、以及访问器克隆对象时的注意事项#

Considerations when cloning objects with prototypes, classes, and accessors

因为对象克隆使用 HTML 结构化克隆算法,所以不保留不可枚举的属性、属性访问器和对象原型。特别是,Buffer 对象在接收方将被读取为纯 Uint8Array,而 JavaScript 类的实例将被克隆为纯 JavaScript 对象。

Because object cloning uses the HTML structured clone algorithm, non-enumerable properties, property accessors, and object prototypes are not preserved. In particular, Buffer objects will be read as plain Uint8Arrays on the receiving side, and instances of JavaScript classes will be cloned as plain JavaScript objects.

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 } 

此限制扩展到许多内置对象,例如全局的 URL 对象:

This limitation extends to many built-in objects, such as the global URL object:

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

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

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

// Prints: { } 

port.hasRef()#

稳定性: 1 - 实验性的

Stability: 1 - Experimental

如果为 true,则 MessagePort 对象将使 Node.js 事件循环保持活动状态。

If true, the MessagePort object will keep the Node.js event loop active.

port.ref()#

unref() 的相反。如果它是唯一剩下的活动句柄(默认行为),则在以前的 unref() 端口上调用 ref() 不会让程序退出。如果端口是 ref() 的,则再次调用 ref() 没有效果。

Opposite of unref(). Calling ref() on a previously unref()ed port does not let the program exit if it's the only active handle left (the default behavior). If the port is ref()ed, calling ref() again has no effect.

如果使用 .on('message') 绑定或删除监听器,则根据事件的监听器是否存在,端口将自动进行 ref()unref()

If listeners are attached or removed using .on('message'), the port is ref()ed and unref()ed automatically depending on whether listeners for the event exist.

port.start()#

开始在此 MessagePort 上接收消息。当将此端口用作事件触发器时,一旦绑定了 'message' 监听器,则会自动调用它

Starts receiving messages on this MessagePort. When using this port as an event emitter, this is called automatically once 'message' listeners are attached.

此方法与 Web MessagePort API 相同。在 Node.js 中,只有在没有事件监听器时才用于忽略消息。Node.js 在处理 .onmessage 方面也有分歧。设置它会自动调用 .start(),但取消设置它会让消息排队,直到设置新的处理程序或端口被丢弃。

This method exists for parity with the Web MessagePort API. In Node.js, it is only useful for ignoring messages when no event listener is present. Node.js also diverges in its handling of .onmessage. Setting it automatically calls .start(), but unsetting it lets messages queue up until a new handler is set or the port is discarded.

port.unref()#

如果这是事件系统中唯一的活动句柄,则在端口上调用 unref() 允许线程退出。如果端口已经 unref(),则再次调用 unref() 无效。

Calling unref() on a port allows the thread to exit if this is the only active handle in the event system. If the port is already unref()ed calling unref() again has no effect.

如果使用 .on('message') 绑定或删除监听器,则根据事件的监听器是否存在,端口将自动进行 ref()unref()

If listeners are attached or removed using .on('message'), the port is ref()ed and unref()ed automatically depending on whether listeners for the event exist.

类:Worker#

Class: Worker

Worker 类代表独立的 JavaScript 执行线程。大多数 Node.js API 都可以在其中使用。

The Worker class represents an independent JavaScript execution thread. Most Node.js APIs are available inside of it.

工作线程环境中的显着差异是:

Notable differences inside a Worker environment are:

在其他 Worker 中创建 Worker 实例是可能的。

Creating Worker instances inside of other Workers is possible.

网络工作线程node:cluster 模块 一样,可以通过线程间消息传递实现双向通信。在内部,Worker 有一对内置的 MessagePort,它们在创建 Worker 时已经相互关联。虽然父端的 MessagePort 对象没有直接暴露,但其功能通过父线程 Worker 对象上的 worker.postMessage()worker.on('message') 事件暴露。

Like Web Workers and the node:cluster module, two-way communication can be achieved through inter-thread message passing. Internally, a Worker has a built-in pair of MessagePorts that are already associated with each other when the Worker is created. While the MessagePort object on the parent side is not directly exposed, its functionalities are exposed through worker.postMessage() and the worker.on('message') event on the Worker object for the parent thread.

要创建自定义消息通道(鼓励使用默认全局通道,因为它有助于分离关注点),用户可以在任一线程上创建 MessageChannel 对象,并通过预先存在的线程将 MessageChannel 上的 MessagePort 之一传递给另一个线程 通道,例如全局通道。

To create custom messaging channels (which is encouraged over using the default global channel because it facilitates separation of concerns), users can create a MessageChannel object on either thread and pass one of the MessagePorts on that MessageChannel to the other thread through a pre-existing channel, such as the global one.

有关如何传递消息以及可以成功通过线程屏障传输的 JavaScript 值类型的更多信息,请参阅 port.postMessage()

See port.postMessage() for more information on how messages are passed, and what kind of JavaScript values can be successfully transported through the thread barrier.

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> 工作线程的主脚本或模块的路径。必须是以 ./../ 开头的绝对路径或相对路径(即相对于当前工作目录)、或者是使用 file:data: 协议的 WHATWG URL 对象使用 data: URL 时,将使用 ECMAScript 模块加载器 根据 MIME 类型解释数据。如果 options.evaltrue,则这是包含 JavaScript 代码(而不是路径)的字符串。

    filename <string> | <URL> The path to the Worker's main script or module. Must be either an absolute path or a relative path (i.e. relative to the current working directory) starting with ./ or ../, or a WHATWG URL object using file: or data: protocol. When using a data: URL, the data is interpreted based on MIME type using the ECMAScript module loader. If options.eval is true, this is a string containing JavaScript code rather than a path.

  • options <Object>

    • argv <any[]> 将被字符串化并附加到工作线程中的 process.argv 的参数列表。这与 workerData 非常相似,但这些值在全局的 process.argv 上可用,就好像它们作为 CLI 选项传给脚本一样。

      argv <any[]> List of arguments which would be stringified and appended to process.argv in the worker. This is mostly similar to the workerData but the values are available on the global process.argv as if they were passed as CLI options to the script.

    • env <Object> 如果设置,则指定工作线程内 process.env 的初始值。worker.SHARE_ENV 作为一个特殊的值,可以用来指定父线程和子线程共享环境变量;在这种情况下,对一个线程的 process.env 对象的更改也会影响另一个线程。默认值:process.env

      env <Object> If set, specifies the initial value of process.env inside the Worker thread. As a special value, worker.SHARE_ENV may be used to specify that the parent thread and the child thread should share their environment variables; in that case, changes to one thread's process.env object affect the other thread as well. Default: process.env.

    • eval <boolean> 如果 true 并且第一个参数是 string,则将构造函数的第一个参数解释为一旦工作线程在线就执行的脚本。

      eval <boolean> If true and the first argument is a string, interpret the first argument to the constructor as a script that is executed once the worker is online.

    • execArgv <string[]> 传给工作线程的 node CLI 选项的列表。不支持 V8 选项(如 --max-old-space-size)和影响进程的选项(如 --title)。如果设置,则此在工作线程内部作为 process.execArgv 提供。默认情况下,选项继承自父线程。

      execArgv <string[]> List of node CLI options passed to the worker. V8 options (such as --max-old-space-size) and options that affect the process (such as --title) are not supported. If set, this is provided as process.execArgv inside the worker. By default, options are inherited from the parent thread.

    • stdin <boolean> 如果设置为 true,则 worker.stdin 提供其内容在工作线程中显示为 process.stdin 的可写流。默认情况下,不提供任何数据。

      stdin <boolean> If this is set to true, then worker.stdin provides a writable stream whose contents appear as process.stdin inside the Worker. By default, no data is provided.

    • stdout <boolean> 如果设置为 true,则 worker.stdout 不会自动通过管道传输到父线程中的 process.stdout

      stdout <boolean> If this is set to true, then worker.stdout is not automatically piped through to process.stdout in the parent.

    • stderr <boolean> 如果设置为 true,则 worker.stderr 不会自动通过管道传输到父线程中的 process.stderr

      stderr <boolean> If this is set to true, then worker.stderr is not automatically piped through to process.stderr in the parent.

    • workerData <any> 任何被克隆并作为 require('node:worker_threads').workerData 可用的 JavaScript 值。克隆如 HTML 结构化克隆算法 中所述发生,如果无法克隆对象(例如,因为它包含 function),则会抛出错误。

      workerData <any> Any JavaScript value that is cloned and made available as require('node:worker_threads').workerData. The cloning occurs as described in the HTML structured clone algorithm, and an error is thrown if the object cannot be cloned (e.g. because it contains functions).

    • trackUnmanagedFds <boolean> 如果设置为 true,则工作线程会跟踪通过 fs.open()fs.close() 管理的原始文件描述符,并在工作线程退出时关闭它们,类似于网络套接字或通过 FileHandle API 管理的文件描述符等其他资源。所有嵌套的 Worker 都会自动继承此选项。默认值:true

      trackUnmanagedFds <boolean> If this is set to true, then the Worker tracks raw file descriptors managed through fs.open() and fs.close(), and closes them when the Worker exits, similar to other resources like network sockets or file descriptors managed through the FileHandle API. This option is automatically inherited by all nested Workers. Default: true.

    • transferList <Object[]> 如果在 workerData 中传入一个或多个类似 MessagePort 的对象,则这些条目需要 transferList 或抛出 ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST。有关详细信息,请参阅 port.postMessage()

      transferList <Object[]> If one or more MessagePort-like objects are passed in workerData, a transferList is required for those items or ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST is thrown. See port.postMessage() for more information.

    • resourceLimits <Object> 新的 JS 引擎实例的一组可选资源限制。达到这些限制会导致 Worker 实例终止。这些限制只影响 JS 引擎,不影响外部数据,包括没有 ArrayBuffer。即使设置了这些限制,如果遇到全局内存不足的情况,进程仍可能会中止。

      resourceLimits <Object> An optional set of resource limits for the new JS engine instance. Reaching these limits leads to termination of the Worker instance. These limits only affect the JS engine, and no external data, including no ArrayBuffers. Even if these limits are set, the process may still abort if it encounters a global out-of-memory situation.

      • maxOldGenerationSizeMb <number> 主堆的最大大小 (以 MB 为单位)。如果设置了命令行参数 --max-old-space-size,它将覆盖此设置。

        maxOldGenerationSizeMb <number> The maximum size of the main heap in MB. If the command-line argument --max-old-space-size is set, it overrides this setting.

      • maxYoungGenerationSizeMb <number> 最近创建的对象的最大堆空间大小。如果设置了命令行参数 --max-semi-space-size,它将覆盖此设置。

        maxYoungGenerationSizeMb <number> The maximum size of a heap space for recently created objects. If the command-line argument --max-semi-space-size is set, it overrides this setting.

      • codeRangeSizeMb <number> 用于生成代码的预分配内存范围的大小。

        codeRangeSizeMb <number> The size of a pre-allocated memory range used for generated code.

      • stackSizeMb <number> 线程的默认最大堆栈大小。较小的值可能会导致工作线程实例无法使用。默认值:4

        stackSizeMb <number> The default maximum stack size for the thread. Small values may lead to unusable Worker instances. Default: 4.

    • name <string> 一个可选的 name 附加到工作进程标题用于调试/识别目的,使最终标题为 [worker ${id}] ${name}。默认值:''

      name <string> An optional name to be appended to the worker title for debugging/identification purposes, making the final title as [worker ${id}] ${name}. Default: ''.

事件:'error'#

Event: 'error'

如果工作线程抛出未捕获的异常,则会触发 'error' 事件。在这种情况下,工作线程被终止。

The 'error' event is emitted if the worker thread throws an uncaught exception. In that case, the worker is terminated.

事件:'exit'#

Event: 'exit'

一旦工作线程停止,则会触发 'exit' 事件。如果工作线程是通过调用 process.exit() 退出的,则 exitCode 参数就是传入的退出码。如果工作线程被终止,则 exitCode 参数为 1

The 'exit' event is emitted once the worker has stopped. If the worker exited by calling process.exit(), the exitCode parameter is the passed exit code. If the worker was terminated, the exitCode parameter is 1.

这是任何 Worker 实例触发的最终事件。

This is the final event emitted by any Worker instance.

事件:'message'#

Event: 'message'

  • value <any> 传输值

    value <any> The transmitted value

当工作线程调用 require('node:worker_threads').parentPort.postMessage() 时,则会触发 'message' 事件。详情请见 port.on('message') 事件。

The 'message' event is emitted when the worker thread has invoked require('node:worker_threads').parentPort.postMessage(). See the port.on('message') event for more details.

Worker 对象上触发 'exit' 事件 之前,从工作线程发送的所有消息都会触发。

All messages sent from the worker thread are emitted before the 'exit' event is emitted on the Worker object.

事件:'messageerror'#

Event: 'messageerror'

当反序列化消息失败时,则会触发 'messageerror' 事件。

The 'messageerror' event is emitted when deserializing a message failed.

事件:'online'#

Event: 'online'

当工作线程开始执行 JavaScript 代码时,则会触发 'online' 事件。

The 'online' event is emitted when the worker thread has started executing JavaScript code.

worker.getHeapSnapshot([options])#

  • options <Object>

    • exposeInternals <boolean> 如果为真,则在堆快照中公开内部结构。默认值:false

      exposeInternals <boolean> If true, expose internals in the heap snapshot. Default: false.

    • exposeNumericValues <boolean> 如果为真,则在人工字段中公开数值。默认值:false

      exposeNumericValues <boolean> If true, expose numeric values in artificial fields. Default: false.

  • 返回:<Promise> 对包含 V8 堆快照的可读流的 promise

    Returns: <Promise> A promise for a Readable Stream containing a V8 heap snapshot

返回工作线程当前状态的 V8 快照的可读流。有关详细信息,请参阅 v8.getHeapSnapshot()

Returns a readable stream for a V8 snapshot of the current state of the Worker. See v8.getHeapSnapshot() for more details.

如果工作线程不再运行(这可能发生在触发 'exit' 事件 之前),则返回的 Promise 会立即被拒绝,并出现 ERR_WORKER_NOT_RUNNING 错误。

If the Worker thread is no longer running, which may occur before the 'exit' event is emitted, the returned Promise is rejected immediately with an ERR_WORKER_NOT_RUNNING error.

worker.performance#

可用于从工作线程实例查询性能信息的对象。类似于 perf_hooks.performance

An object that can be used to query performance information from a worker instance. Similar to perf_hooks.performance.

performance.eventLoopUtilization([utilization1[, utilization2]])#
  • utilization1 <Object> 上一次调用 eventLoopUtilization() 的结果。

    utilization1 <Object> The result of a previous call to eventLoopUtilization().

  • utilization2 <Object>utilization1 之前调用 eventLoopUtilization() 的结果。

    utilization2 <Object> The result of a previous call to eventLoopUtilization() prior to utilization1.

  • 返回 <Object>

    Returns <Object>

perf_hooks eventLoopUtilization() 相同的调用,除了返回 worker 实例的值。

The same call as perf_hooks eventLoopUtilization(), except the values of the worker instance are returned.

一个区别是,与主线程不同,工作线程内的引导是在事件循环内完成的。因此,一旦工作线程的脚本开始执行,事件循环的利用率将立即可用。

One difference is that, unlike the main thread, bootstrapping within a worker is done within the event loop. So the event loop utilization is immediately available once the worker's script begins execution.

不增加的 idle 时间并不表示工作线程卡在引导中。下面的示例展示了工作线程的整个生命周期从未累积任何 idle 时间,但仍然能够处理消息。

An idle time that does not increase does not indicate that the worker is stuck in bootstrap. The following examples shows how the worker's entire lifetime never accumulates any idle time, but is still be able to process messages.

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

worker 的事件循环利用率仅在 'online' 事件 触发后可用,如果在此之前或 'exit' 事件 之后调用,则所有属性都具有 0 的值。

The event loop utilization of a worker is available only after the 'online' event emitted, and if called before this, or after the 'exit' event, then all properties have the value of 0.

worker.postMessage(value[, transferList])#

向通过 require('node:worker_threads').parentPort.on('message') 接收到的工作线程发送消息。有关详细信息,请参阅 port.postMessage()

Send a message to the worker that is received via require('node:worker_threads').parentPort.on('message'). See port.postMessage() for more details.

worker.ref()#

unref() 相反,如果它是唯一剩下的活动句柄(默认行为),则在之前 unref() worker 上调用 ref() 不会让程序退出。如果工作线程是 ref() 的,则再次调用 ref() 没有效果。

Opposite of unref(), calling ref() on a previously unref()ed worker does not let the program exit if it's the only active handle left (the default behavior). If the worker is ref()ed, calling ref() again has no effect.

worker.resourceLimits#

为此工作线程提供了一组 JS 引擎资源约束。如果将 resourceLimits 选项传给 Worker 构造函数,则这与其值匹配。

Provides the set of JS engine resource constraints for this Worker thread. If the resourceLimits option was passed to the Worker constructor, this matches its values.

如果工作线程已经停止,则返回值是空对象。

If the worker has stopped, the return value is an empty object.

worker.stderr#

这是包含工作线程内写入 process.stderr 的数据的可读流。如果 stderr: true 没有传给 Worker 构造函数,则数据将通过管道传输到父线程的 process.stderr 流。

This is a readable stream which contains data written to process.stderr inside the worker thread. If stderr: true was not passed to the Worker constructor, then data is piped to the parent thread's process.stderr stream.

worker.stdin#

如果将 stdin: true 传给 Worker 构造函数,则这是可写流。写入此流的数据将在工作线程中作为 process.stdin 可用。

If stdin: true was passed to the Worker constructor, this is a writable stream. The data written to this stream will be made available in the worker thread as process.stdin.

worker.stdout#

这是包含工作线程内写入 process.stdout 的数据的可读流。如果 stdout: true 没有传给 Worker 构造函数,则数据将通过管道传输到父线程的 process.stdout 流。

This is a readable stream which contains data written to process.stdout inside the worker thread. If stdout: true was not passed to the Worker constructor, then data is piped to the parent thread's process.stdout stream.

worker.terminate()#

尽快停止工作线程中的所有 JavaScript 执行。返回在触发 'exit' 事件 时完成的退出代码的 Promise。

Stop all JavaScript execution in the worker thread as soon as possible. Returns a Promise for the exit code that is fulfilled when the 'exit' event is emitted.

worker.threadId#

引用线程的整数标识符。在工作线程内部,它作为 require('node:worker_threads').threadId 可用。此值对于单个进程中的每个 Worker 实例都是唯一的。

An integer identifier for the referenced thread. Inside the worker thread, it is available as require('node:worker_threads').threadId. This value is unique for each Worker instance inside a single process.

worker.unref()#

如果这是事件系统中唯一的活动句柄,则在工作线程上调用 unref() 允许线程退出。如果工作线程已经 unref(),则再次调用 unref() 无效。

Calling unref() on a worker allows the thread to exit if this is the only active handle in the event system. If the worker is already unref()ed calling unref() again has no effect.

注意事项#

Notes

stdio 的同步阻塞#

Synchronous blocking of stdio

Worker 利用通过 <MessagePort> 传递的消息来实现与 stdio 的交互。这意味着来自 Workerstdio 输出可能会被接收端的同步代码阻塞,这会阻塞 Node.js 事件循环。

Workers utilize message passing via <MessagePort> to implement interactions with stdio. This means that stdio output originating from a Worker can get blocked by synchronous code on the receiving end that is blocking the Node.js event loop.

import {
  Worker,
  isMainThread,
} from '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');
}

从预加载脚本启动工作线程#

Launching worker threads from preload scripts

从预加载脚本(使用 -r 命令行标志加载和运行的脚本)启动工作线程时要小心。除非显式设置了 execArgv 选项,否则新的工作线程会自动从正在运行的进程继承命令行标志,并将预加载与主线程相同的预加载脚本。如果预加载脚本无条件地启动工作线程,则每个衍生的线程都会衍生另一个直到应用崩溃。

Take care when launching worker threads from preload scripts (scripts loaded and run using the -r command line flag). Unless the execArgv option is explicitly set, new Worker threads automatically inherit the command line flags from the running process and will preload the same preload scripts as the main thread. If the preload script unconditionally launches a worker thread, every thread spawned will spawn another until the application crashes.