Skip to content

Worker 线程

[稳定: 2 - 稳定]

稳定: 2 稳定性: 2 - 稳定

源代码: lib/worker_threads.js

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

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

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

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

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

if (isMainThread) {
  module.exports = function parseJSAsync(script) {
    return new Promise((resolve, reject) => {
      const worker = new Worker(__filename, {
        workerData: script,
      })
      worker.on('message', resolve)
      worker.on('error', reject)
      worker.on('exit', code => {
        if (code !== 0) reject(new Error(`Worker 停止,退出代码 ${code}`))
      })
    })
  }
} else {
  const { parse } = require('some-js-parsing-library')
  const script = workerData
  parentPort.postMessage(parse(script))
}

上面的例子为每个 parseJSAsync() 调用产生一个 Worker 线程。在实践中,对这类任务使用 Worker 池。否则,创建 Worker 的开销可能会超过其好处。

在实现 Worker 池时,使用 AsyncResource API 来告知诊断工具(例如,提供异步堆栈跟踪)任务及其结果之间的关联。在 async_hooks 文档中查看 "使用 AsyncResource 创建 Worker 线程池" 以了解示例实现。

Worker 线程默认继承非进程特定的选项。请参考 Worker 构造函数选项 来了解如何自定义 Worker 线程选项,特别是 argvexecArgv 选项。

worker.getEnvironmentData(key)

[历史]

版本变更
v17.5.0, v16.15.0移除实验性标记。
v15.12.0, v14.18.0新增于:v15.12.0, v14.18.0

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

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

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

worker.isMainThread

新增于:v10.5.0

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

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

if (isMainThread) {
  // 这会在 Worker 实例中重新加载当前文件。
  new Worker(__filename)
} else {
  console.log('Worker内部!')
  console.log(isMainThread) // 打印 'false'。
}

worker.markAsUntransferable(object)

新增于:v14.5.0, v12.19.0

将对象标记为不可传输。如果 object 出现在 port.postMessage() 调用的传输列表中,则会抛出错误。如果 object 是一个原始值,则此操作为无操作。

特别是,这对于可以克隆而不是传输并且在发送方由其他对象使用的对象很有意义。例如,Node.js 使用此方法标记其 Buffer 使用的 ArrayBuffer

此操作无法撤消。

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

const pooledBuffer = new ArrayBuffer(8)
const typedArray1 = new Uint8Array(pooledBuffer)
const typedArray2 = new Float64Array(pooledBuffer)

markAsUntransferable(pooledBuffer)

const { port1 } = new MessageChannel()
try {
  // 这将抛出错误,因为 pooledBuffer 不可传输。
  port1.postMessage(typedArray1, [typedArray1.buffer])
} catch (error) {
  // error.name === 'DataCloneError'
}

// 下一行打印 typedArray1 的内容——它仍然拥有其内存并且尚未被传输。如果没有
// `markAsUntransferable()`,这将打印一个空的 Uint8Array,并且
// postMessage 调用将成功。
// typedArray2 也完好无损。
console.log(typedArray1)
console.log(typedArray2)

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

worker.isMarkedAsUntransferable(object)

新增于: v21.0.0

检查对象是否使用 markAsUntransferable() 标记为不可传输。

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

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

isMarkedAsUntransferable(pooledBuffer) // 返回 true。

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

worker.markAsUncloneable(object)

新增于: v23.0.0

  • object <any> 任意 JavaScript 值。

将对象标记为不可克隆。如果 objectport.postMessage() 调用中用作 message,则会抛出错误。如果 object 是原始值,则此操作无效。

这不会影响 ArrayBuffer 或任何类似 Buffer 的对象。

此操作无法撤消。

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

const anyObject = { foo: 'bar' }
markAsUncloneable(anyObject)
const { port1 } = new MessageChannel()
try {
  // 这将抛出错误,因为 anyObject 不可克隆。
  port1.postMessage(anyObject)
} catch (error) {
  // error.name === 'DataCloneError'
}

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

worker.moveMessagePortToContext(port, contextifiedSandbox)

新增于:v11.13.0

MessagePort 传输到不同的 vm 上下文。原始 port 对象将无法使用,返回的 MessagePort 实例将取代它。

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

但是,创建的 MessagePort 不再继承自 EventTarget,并且只能使用 port.onmessage() 来接收使用它的事件。

worker.parentPort

新增于: v10.5.0

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

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

if (isMainThread) {
  const worker = new Worker(__filename)
  worker.once('message', message => {
    console.log(message) // 打印 'Hello, world!'。
  })
  worker.postMessage('Hello, world!')
} else {
  // 当接收到来自父线程的消息时,将其发送回去:
  parentPort.once('message', message => {
    parentPort.postMessage(message)
  })
}

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

新增于: v22.5.0

[稳定性: 1 - 实验性]

稳定性: 1 1 - 积极开发中

将值发送到由其线程 ID 标识的另一个工作线程。

如果目标线程没有 workerMessage 事件的监听器,则操作将抛出 ERR_WORKER_MESSAGING_FAILED 错误。

如果目标线程在处理 workerMessage 事件时抛出错误,则操作将抛出 ERR_WORKER_MESSAGING_ERRORED 错误。

当目标线程不是当前线程的直接父级或子级时,应使用此方法。如果两个线程是父子关系,请使用 require('node:worker_threads').parentPort.postMessage()worker.postMessage() 来实现线程间的通信。

下面的示例演示了 postMessageToThread 的用法:它创建 10 个嵌套线程,最后一个线程将尝试与主线程通信。

js
import { fileURLToPath } from 'node:url'
import process from 'node:process'
import { postMessageToThread, threadId, workerData, Worker } from 'node:worker_threads'

const channel = new BroadcastChannel('sync')
const level = workerData?.level ?? 0

if (level < 10) {
  const worker = new Worker(fileURLToPath(import.meta.url), {
    workerData: { level: level + 1 },
  })
}

if (level === 0) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value)
    postMessageToThread(source, { message: 'pong' })
  })
} else if (level === 10) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value)
    channel.postMessage('done')
    channel.close()
  })

  await postMessageToThread(0, { message: 'ping' })
}

channel.onmessage = channel.close
js
const { postMessageToThread, threadId, workerData, Worker } = require('node:worker_threads')

const channel = new BroadcastChannel('sync')
const level = workerData?.level ?? 0

if (level < 10) {
  const worker = new Worker(__filename, {
    workerData: { level: level + 1 },
  })
}

if (level === 0) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value)
    postMessageToThread(source, { message: 'pong' })
  })
} else if (level === 10) {
  process.on('workerMessage', (value, source) => {
    console.log(`${source} -> ${threadId}:`, value)
    channel.postMessage('done')
    channel.close()
  })

  postMessageToThread(0, { message: 'ping' })
}

channel.onmessage = channel.close

worker.receiveMessageOnPort(port)

[历史]

版本变更
v15.12.0port 参数现在也可以指代 BroadcastChannel
v12.3.0v12.3.0 中添加

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

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

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

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

worker.resourceLimits

新增于:v13.2.0, v12.16.0

提供此 Worker 线程内部的 JS 引擎资源限制集。如果将 resourceLimits 选项传递给 Worker 构造函数,则此值与其值匹配。

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

worker.SHARE_ENV

新增于:v11.14.0

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

js
const { Worker, SHARE_ENV } = require('node:worker_threads')
new Worker('process.env.SET_IN_WORKER = "foo"', { eval: true, env: SHARE_ENV }).on('exit', () => {
  console.log(process.env.SET_IN_WORKER) // 打印 'foo'。
})

worker.setEnvironmentData(key[, value])

[历史]

版本变更
v17.5.0, v16.15.0移除实验性标记。
v15.12.0, v14.18.0v15.12.0, v14.18.0 版本中添加
  • key <任意值> 任何可克隆的任意 JavaScript 值,可用作 <Map> 的键。
  • value <任意值> 任何可克隆的任意 JavaScript 值,它会被克隆并自动传递给所有新的 Worker 实例。如果 value 传递为 undefined,则任何先前为 key 设置的值都将被删除。

worker.setEnvironmentData() API 设置当前线程和从当前上下文生成的全部新 Worker 实例中 worker.getEnvironmentData() 的内容。

worker.threadId

添加到:v10.5.0

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

worker.workerData

新增于: v10.5.0

一个任意的 JavaScript 值,包含传递给此线程 Worker 构造函数的数据的克隆。

数据克隆方式如同使用 postMessage() 一样,遵循 HTML 结构化克隆算法

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

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

类: BroadcastChannel extends EventTarget

[历史]

版本变更
v18.0.0移除实验性标识。
v15.4.0新增于: v15.4.0

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

js
'use strict'

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

const bc = new BroadcastChannel('hello')

if (isMainThread) {
  let c = 0
  bc.onmessage = event => {
    console.log(event.data)
    if (++c === 10) bc.close()
  }
  for (let n = 0; n < 10; n++) new Worker(__filename)
} else {
  bc.postMessage('hello from every worker')
  bc.close()
}

new BroadcastChannel(name)

新增于: v15.4.0

  • name <any> 要连接的通道名称。允许任何可以使用 ${name} 转换为字符串的 JavaScript 值。

broadcastChannel.close()

新增于: v15.4.0

关闭 BroadcastChannel 连接。

broadcastChannel.onmessage

新增于: v15.4.0

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

broadcastChannel.onmessageerror

新增于: v15.4.0

  • 类型: <Function> 当接收到的消息无法反序列化时调用。

broadcastChannel.postMessage(message)

新增于: v15.4.0

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

broadcastChannel.ref()

新增于:v15.4.0

unref() 相反。对之前已 unref() 的 BroadcastChannel 调用 ref() 不会 允许程序在它是唯一剩余的活动句柄时退出(默认行为)。如果端口已 ref(),再次调用 ref() 则无效。

broadcastChannel.unref()

新增于:v15.4.0

对 BroadcastChannel 调用 unref() 允许线程在它是事件系统中唯一活动句柄时退出。如果 BroadcastChannel 已经 unref(),再次调用 unref() 则无效。

类:MessageChannel

新增于:v10.5.0

worker.MessageChannel 类的实例表示一个异步的、双向的通信通道。MessageChannel 本身没有方法。new MessageChannel() 会产生一个具有 port1port2 属性的对象,它们指向链接的 MessagePort 实例。

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

const { port1, port2 } = new MessageChannel()
port1.on('message', message => console.log('received', message))
port2.postMessage({ foo: 'bar' })
// 从 `port1.on('message')` 监听器打印:received { foo: 'bar' }

类: MessagePort

[历史]

版本变更
v14.7.0此类现在继承自 EventTarget 而不是 EventEmitter
v10.5.0新增于: v10.5.0

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

此实现与 浏览器 MessagePort 相匹配。

事件: 'close'

新增于: v10.5.0

当通道任一侧断开连接时,将发出 'close' 事件。

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

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

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

事件:'message'

新增于:v10.5.0

'message' 事件针对任何传入的消息发出,包含 port.postMessage() 的克隆输入。

此事件的侦听器接收作为参数传递给 postMessage()value 的克隆,以及无其他参数。

事件:'messageerror'

新增于:v14.5.0, v12.19.0

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

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

port.close()

新增于:v10.5.0

禁用连接任一侧的后续消息发送。当不再需要通过此 MessagePort 进行通信时,可以调用此方法。

'close' 事件 将在通道中涉及的两个 MessagePort 实例上发出。

port.postMessage(value[, transferList])

[历史]

版本变更
v21.0.0传输列表中存在不可传输对象时将抛出错误。
v15.6.0X509Certificate 添加到可克隆类型列表中。
v15.0.0CryptoKey 添加到可克隆类型列表中。
v15.14.0, v14.18.0将 'BlockList' 添加到可克隆类型列表中。
v15.9.0, v14.18.0将 'Histogram' 类型添加到可克隆类型列表中。
v14.5.0, v12.19.0KeyObject 添加到可克隆类型列表中。
v14.5.0, v12.19.0FileHandle 添加到可传输类型列表中。
v10.5.0新增于:v10.5.0

将 JavaScript 值发送到此通道的接收方。value 的传输方式与 HTML 结构化克隆算法 兼容。

具体来说,与 JSON 的显著区别在于:

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

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

const circularData = {}
circularData.foo = circularData
// 输出:{ foo: [Circular] }
port2.postMessage(circularData)

transferList 可以是 ArrayBufferMessagePortFileHandle 对象的列表。传输后,它们将不再可在通道的发送端使用(即使它们不包含在 value 中)。与 子进程 不同,目前不支持传输网络套接字等句柄。

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

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

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

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

const uint8Array = new Uint8Array([1, 2, 3, 4])
// 这会发布 `uint8Array` 的副本:
port2.postMessage(uint8Array)
// 这不会复制数据,但会使 `uint8Array` 无法使用:
port2.postMessage(uint8Array, [uint8Array.buffer])

// `sharedUint8Array` 的内存可从原始副本和 `.on('message')` 接收到的副本访问:
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4))
port2.postMessage(sharedUint8Array)

// 这会将新创建的消息端口传输到接收方。
// 例如,这可以用于在同一父线程的多个 `Worker` 线程之间创建通信通道。
const otherChannel = new MessageChannel()
port2.postMessage({ port: otherChannel.port1 }, [otherChannel.port1])

消息对象会立即克隆,并且可以在发布后修改,而不会产生副作用。

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

传输 TypedArrays 和 Buffers 时需要考虑的问题

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

js
const ab = new ArrayBuffer(10)

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

console.log(u2.length) // 打印 5

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

console.log(u2.length) // 打印 0

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

可以使用 markAsUntransferable()ArrayBuffer 标记为始终克隆且从不传输。

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

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

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

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

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

js
const b = Symbol('b')

class Foo {
  #a = 1
  constructor() {
    this[b] = 2
    this.c = 3
  }

  get d() {
    return 4
  }
}

const { port1, port2 } = new MessageChannel()

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

port2.postMessage(new Foo())

// 打印:{ c: 3 }

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

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

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

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

// 打印:{ }

port.hasRef()

新增于:v18.1.0, v16.17.0

[稳定性:1 - 实验性]

稳定性:1 稳定性:1 - 实验性

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

port.ref()

新增于:v10.5.0

unref() 的反向操作。如果之前调用过 unref(),再次调用 ref() 不会让程序在它是唯一剩余的活动句柄时退出(默认行为)。如果 portref(),再次调用 ref() 不会产生任何影响。

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

port.start()

新增于:v10.5.0

开始接收此 MessagePort 上的消息。当使用此端口作为事件发射器时,一旦附加了 'message' 监听器,就会自动调用此方法。

此方法的存在是为了与 Web MessagePort API 保持一致。在 Node.js 中,它仅在没有事件监听器存在时用于忽略消息。Node.js 在处理 .onmessage 方面也存在差异。设置它会自动调用 .start(),但取消设置它会让消息排队,直到设置新的处理程序或丢弃端口。

port.unref()

新增于:v10.5.0

调用端口上的 unref() 方法允许线程在它是事件系统中唯一活动句柄时退出。如果端口已 unref(),再次调用 unref() 将不起作用。

如果使用 .on('message') 附加或删除监听器,则端口会根据事件监听器是否存在自动 ref()unref()

类:Worker

新增于:v10.5.0

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

在 Worker 环境中的显著区别:

可以在其他 Worker 中创建 Worker 实例。

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

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

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

js
const assert = require('node:assert')
const { Worker, MessageChannel, MessagePort, isMainThread, parentPort } = require('node:worker_threads')
if (isMainThread) {
  const worker = new Worker(__filename)
  const subChannel = new MessageChannel()
  worker.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1])
  subChannel.port2.on('message', value => {
    console.log('received:', value)
  })
} else {
  parentPort.once('message', value => {
    assert(value.hereIsYourPort instanceof MessagePort)
    value.hereIsYourPort.postMessage('the worker is sending this')
    value.hereIsYourPort.close()
  })
}

new Worker(filename[, options])

[历史]

版本变更
v19.8.0, v18.16.0添加了 name 选项的支持,允许为 worker 标题添加名称以进行调试。
v14.9.0filename 参数可以使用 data: 协议的 WHATWG URL 对象。
v14.9.0trackUnmanagedFds 选项默认设置为 true
v14.6.0, v12.19.0引入了 trackUnmanagedFds 选项。
v13.13.0, v12.17.0引入了 transferList 选项。
v13.12.0, v12.17.0filename 参数可以使用 file: 协议的 WHATWG URL 对象。
v13.4.0, v12.16.0引入了 argv 选项。
v13.2.0, v12.16.0引入了 resourceLimits 选项。
v10.5.0v10.5.0 版本新增
  • filename <字符串> | <URL> Worker 主脚本或模块的路径。必须是绝对路径或相对路径(即相对于当前工作目录),以 ./../ 开头,或者是一个使用 file:data: 协议的 WHATWG URL 对象。当使用 data: URL 时,数据将根据 MIME 类型使用 ECMAScript 模块加载器 进行解释。如果 options.evaltrue,则这是一个包含 JavaScript 代码的字符串,而不是路径。

  • options <对象>

    • argv <任意数组> 参数列表,这些参数将被字符串化并追加到 worker 中的 process.argv。这与 workerData 非常相似,但是这些值在全局 process.argv 上可用,就像它们作为 CLI 选项传递给脚本一样。

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

    • eval <布尔值> 如果为 true 且第一个参数是 string,则将构造函数的第一个参数解释为在 worker 在线后执行一次的脚本。

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

    • stdin <布尔值> 如果设置为 true,则 worker.stdin 提供一个可写流,其内容显示为 Worker 内部的 process.stdin。默认情况下,不提供任何数据。

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

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

    • workerData <任意类型> 克隆并作为 require('node:worker_threads').workerData 提供的任何 JavaScript 值。克隆过程如 HTML 结构化克隆算法 中所述,如果对象无法克隆(例如,因为它包含 function),则会抛出错误。

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

    • transferList <对象数组> 如果在 workerData 中传递了一个或多个 MessagePort 类型的对象,则需要为这些项目提供 transferList,否则会抛出 ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST 错误。有关更多信息,请参阅 port.postMessage()

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

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

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

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

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

    • name <字符串> 一个可选的 name,用于在调试/识别目的附加到 worker 标题,使最终标题为 [worker ${id}] ${name}默认值: ''

事件: 'error'

新增于: v10.5.0

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

事件: 'exit'

新增于: v10.5.0

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

这是任何 Worker 实例发出的最后一个事件。

事件: 'message'

新增于: v10.5.0

当工作线程调用 require('node:worker_threads').parentPort.postMessage() 时,会发出 'message' 事件。有关更多详细信息,请参阅 port.on('message') 事件。

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

事件: 'messageerror'

新增于: v14.5.0, v12.19.0

当反序列化消息失败时,将发出 'messageerror' 事件。

事件: 'online'

新增于: v10.5.0

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

worker.getHeapSnapshot([options])

[历史记录]

版本变更
v19.1.0支持配置堆快照的选项。
v13.9.0, v12.17.0新增于: v13.9.0, v12.17.0
  • options <Object>

    • exposeInternals <boolean> 如果为 true,则在堆快照中公开内部信息。默认值: false
    • exposeNumericValues <boolean> 如果为 true,则在人工字段中公开数值。默认值: false
  • 返回值: <Promise> 一个包含 V8 堆快照的可读流的 Promise

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

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

worker.performance

新增于:v15.1.0, v14.17.0, v12.22.0

一个可用于查询 worker 实例性能信息的 对象。类似于 perf_hooks.performance

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

新增于:v15.1.0, v14.17.0, v12.22.0

  • utilization1 <Object> eventLoopUtilization() 方法上一次调用的结果。
  • utilization2 <Object>utilization1 之前,eventLoopUtilization() 方法上一次调用的结果。
  • 返回值: <Object>

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

不同之处在于,与主线程不同,worker 内的引导是在事件循环中完成的。因此,一旦 worker 的脚本开始执行,事件循环利用率就会立即可用。

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

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

if (isMainThread) {
  const worker = new Worker(__filename)
  setInterval(() => {
    worker.postMessage('hi')
    console.log(worker.performance.eventLoopUtilization())
  }, 100).unref()
  return
}

parentPort.on('message', () => console.log('msg')).unref()
;(function r(n) {
  if (--n < 0) return
  const t = Date.now()
  while (Date.now() - t < 300);
  setImmediate(r, n)
})(10)

只有在发出 'online' 事件 之后,才能使用 worker 的事件循环利用率,如果在此之前或在 'exit' 事件 之后调用,则所有属性的值都为 0

worker.postMessage(value[, transferList])

新增于: v10.5.0

向工作线程发送消息,该消息将通过 require('node:worker_threads').parentPort.on('message') 接收。更多详情请参见 port.postMessage()

worker.ref()

新增于: v10.5.0

unref() 相反,如果之前对工作线程调用过 unref(),则调用 ref() 并不会让程序在它是唯一剩余的活动句柄时退出(默认行为)。如果工作线程已 ref(),则再次调用 ref() 不会产生任何效果。

worker.resourceLimits

新增于: v13.2.0, v12.16.0

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

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

worker.stderr

新增于:v10.5.0

这是一个可读流,包含写入工作线程内部的 process.stderr 的数据。如果未将 stderr: true 传递给 Worker 构造函数,则数据将被管道传输到父线程的 process.stderr 流。

worker.stdin

新增于:v10.5.0

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

worker.stdout

新增于:v10.5.0

这是一个可读流,包含写入工作线程内部的 process.stdout 的数据。如果未将 stdout: true 传递给 Worker 构造函数,则数据将被管道传输到父线程的 process.stdout 流。

worker.terminate()

[历史]

版本变更
v12.5.0此函数现在返回一个 Promise。传递回调已弃用,并且在此版本之前是无用的,因为 Worker 实际上是同步终止的。终止现在是一个完全异步的操作。
v10.5.0新增于:v10.5.0

尽快停止工作线程中的所有 JavaScript 执行。返回一个 Promise,用于在发出 'exit' 事件 时 fulfilled 的退出代码。

worker.threadId

新增于:v10.5.0

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

worker.unref()

新增于:v10.5.0

调用 worker 上的 unref() 方法允许线程在它是事件系统中唯一活动句柄时退出。如果 worker 已经 unref() 了,再次调用 unref() 不会有任何效果。

注意

标准输入输出的同步阻塞

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

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

if (isMainThread) {
  new Worker(new URL(import.meta.url))
  for (let n = 0; n < 1e10; n++) {
    // 循环模拟工作。
  }
} else {
  // 此输出将被主线程中的 for 循环阻塞。
  console.log('foo')
}
js
'use strict'

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

if (isMainThread) {
  new Worker(__filename)
  for (let n = 0; n < 1e10; n++) {
    // 循环模拟工作。
  }
} else {
  // 此输出将被主线程中的 for 循环阻塞。
  console.log('foo')
}

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

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