Skip to main content
Worker API 仍处于实验阶段(尤其是终止 workers 功能)。我们正在积极改进这一点。
借助 Worker,你可以启动一个在独立线程上运行的新 JavaScript 实例,并与其进行通信,同时与主线程共享 I/O 资源。 Bun 实现了 Web Workers API 的一个精简版本,并进行了扩展,使其更适用于服务器端场景。与 Bun 的其他部分一样,Worker 无需额外的构建步骤即可支持 CommonJS、ES 模块、TypeScript、JSX 和 TSX。

创建一个 Worker

与浏览器中一样,Worker 是全局对象。使用它可以创建一个新的工作线程。

在主线程中

index.ts

工作线程

worker.ts
为防止在使用 self 时出现 TypeScript 报错,请在工作线程文件顶部添加以下声明:
你可以在工作线程代码中使用 import 和 export 语法。与浏览器不同,使用 ES 模块时无需传递 {type: "module"}。 如果工作线程的脚本无法解析,Bun 会在 Worker 对象上触发一个 "error" 事件。
Bun 会相对于项目根目录解析传递给 Worker 的说明符(就像输入 bun ./path/to/file.js 一样)。

preload - 在 worker 启动前加载模块

将模块说明符数组传递给 preload 选项,以便在工作线程自身的代码运行前加载这些模块,其行为类似于 --preload CLI 参数。可以使用它来加载必须首先加载的代码,例如 OpenTelemetry、Sentry 或 DataDog。
index.ts
你也可以将单个字符串传递给 preload 选项:
index.ts

blob: URL

你也可以将 blob: URL 传递给 Worker,从字符串或其他内存中的源创建工作线程。
与 Bun 的其他部分一样,从 blob: URL 创建的工作线程支持 TypeScript、JSX 和其他文件类型。要告知 Bun 源代码是 TypeScript,请在 Blob 上设置 type,或将 filename 传递给 File 构造函数。

"open" 事件

Bun 会在工作线程创建完成并准备好接收消息时触发 "open" 事件。(浏览器中不存在此事件。)
index.ts
在工作线程的脚本运行前发送的消息会进入队列,直到脚本运行后才会发送,因此你无需等待 "open" 事件再发送消息。这些消息会传递给脚本运行期间安装的处理器。如果脚本没有安装处理器,消息就会被丢弃。 与浏览器中一样,无论是否已存在 "message" 处理器,消息都会在工作线程脚本的同步部分运行后立即分发;如果分发消息时没有处理器,消息就会被丢弃。请在第一个顶层 await 之前安装 self.onmessage / self.addEventListener("message", ...):
worker.ts
静态导入的模块中的顶层 await 也同样适用。静态导入会在工作线程自身的代码运行前求值,因此,如果导入图中的任何模块在顶层使用了 await,消息就会在工作线程的第一行代码运行前开始分发,此时再在第一行安装处理器已经太晚了。请在安装处理器后使用动态 import() 加载这类模块,或使用 node:worker_threads。 node:worker_threads 在这方面与 Bun 和 Node.js 中的行为一致:parentPort 会将消息保留在队列中,直到附加 "message" 监听器(以及每次最后一个监听器被移除时),因此,在 parentPort.on("message", ...) 之前运行 await init() 的工作线程仍会按顺序接收所有消息。

使用 postMessage 发送消息

要发送消息,请使用 worker.postMessage 和 self.postMessage。Bun 使用 HTML Structured Clone Algorithm 序列化消息。

性能优化

Bun 针对常见数据类型的 postMessage 提供了快速路径: 字符串快速路径 - 发送纯字符串时,Bun 完全绕过结构化克隆算法,因此不会产生序列化开销。 简单对象快速路径 - 对于只包含原始值(字符串、数字、布尔值、null、undefined)的普通对象,Bun 会直接存储属性,而无需进行完整的结构化克隆。 简单对象快速路径适用条件:
  • 是普通对象且没有修改原型链
  • 只包含可枚举、可配置的数据属性
  • 没有索引属性或 getter/setter 方法
  • 所有属性值都是原始类型或字符串
有了这些快速路径,Bun 的 postMessage 性能可提升 2 到 241 倍,消息长度不再显著影响性能。 Bun(含快速路径):
Node.js v24.6.0(对比):
接收消息可使用 worker 或主线程的 message 事件处理器:

终止工作线程

Worker 实例会在其事件循环中没有剩余工作时自动终止。在全局对象或任何 MessagePort 上附加 "message" 监听器会使事件循环保持活动状态。要强制终止 Worker,请调用 worker.terminate()。
index.ts
调用 worker.terminate() 会使工作线程尽快退出。

process.exit()

工作线程可以通过 process.exit() 自行终止。这不会终止主进程。与 Node.js 中一样,process.on('beforeExit', callback) 和 process.on('exit', callback) 会在工作线程而非主线程上触发。Bun 会将退出代码传递给 "close" 事件。

"close" 事件

Bun 会在工作线程被标记为终止时触发 "close" 事件。工作线程完全退出可能需要一些时间。CloseEvent 包含传递给 process.exit() 的退出代码;如果因其他原因关闭,则为 0。
index.ts
此事件在浏览器中不存在。

生命周期管理

默认情况下,活跃的 Worker 会保持主(创建它的)进程处于运行状态,因此 setTimeout 和 promise 等异步任务会使进程保持运行。附加 message 监听器也会使 Worker 保持运行。

worker.unref()

要阻止正在运行的 worker 使进程保持运行,请调用 worker.unref()。这会将 worker 的生命周期与主进程的生命周期解耦,其行为与 Node.js 的 worker_threads 一致。
index.ts
浏览器不支持 worker.unref()。

worker.ref()

要使进程保持运行,直到 Worker 终止,请调用 worker.ref()。默认情况下,Worker 会被引用;被引用的 worker 仍需要其事件循环中存在某些内容(例如 "message" 监听器)才能继续运行。
index.ts
或者,也可以在传递给 Worker 的 options 对象中传入 ref: false,这与调用 worker.unref() 等效:
index.ts
浏览器不支持 worker.ref()。

使用 smol 模式降低内存使用

Bun 的 Worker 支持 smol 模式,可以降低内存使用,但会牺牲性能。要启用该模式,请在 Worker 构造函数的 options 对象中传入 smol: true。
index.ts
设置 smol: true 会将 JSC::HeapSize 设置为 Small,而非默认的 Large。

环境数据共享

使用 setEnvironmentData() 和 getEnvironmentData() 在主线程和工作线程间共享数据。
index.ts

Worker 事件

使用 process.on() 监听 worker 创建事件:
index.ts

Bun.isMainThread

检查 Bun.isMainThread 以判断当前是否处于主线程。