Worker 允许你启动并与一个运行在独立线程上的新的 JavaScript 实例通信,同时与主线程共享 I/O 资源。
Bun 实现了 Web Workers API 的一个简化版本,同时添加了扩展,使其更适合服务器端的使用场景。与 Bun 的其他部分一样,Bun 中的 Worker 原生支持 CommonJS、ES 模块、TypeScript、JSX、TSX 等,无需额外构建步骤。
创建一个 Worker
与浏览器中一样,Worker 是全局对象。使用它可以创建一个新的工作线程。
在主线程中
工作线程
worker.ts
self 时出现 TypeScript 报错,请在工作线程文件顶部添加以下声明:
import 和 export 语法。与浏览器不同的是,不需要指定 {type: "module"} 来使用 ES 模块。
为了简化错误处理,初始脚本会在调用 new Worker(url) 时立即解析。
Worker 的路径会相对于项目根目录解析(类似于在命令行运行 bun ./path/to/file.js)。
preload - 在 worker 启动前加载模块
你可以通过 preload 选项传递一个模块路径数组,以便在 worker 启动前加载这些模块。这在你想确保某些代码(如加载 OpenTelemetry、Sentry、DataDog 等)总是在应用启动前加载时非常有用。
--preload CLI 参数一样,preload 选项会在 worker 启动前处理。
你也可以向 preload 选项传入单个字符串:
blob: URL
你也可以向 Worker 传入 blob: URL。这对于从字符串或其他来源创建 worker 很有用。
blob: URL 创建的 workers 也默认支持 TypeScript、JSX 等文件类型。你可以通过设置 type 或在 File 构造函数里传入文件名来告诉它使用 TypeScript。
"open" 事件
当 worker 创建完成并准备好接收消息时,会发出 "open" 事件。你可以使用该事件向 worker 发送初始消息。(此事件在浏览器中不存在)
"open" 事件即可发送消息。
使用 postMessage 发送消息
要发送消息,使用 worker.postMessage 和 self.postMessage。这利用了 HTML 结构化克隆算法。
性能优化
Bun 为postMessage 包含了优化的快速路径,以显著提升常用数据类型的性能:
字符串快速路径 - 当只发送纯字符串时,Bun 直接绕过结构化克隆算法,显著减少序列化开销。
简单对象快速路径 - 对于仅包含原始值(字符串、数字、布尔值、null 和 undefined)的普通对象,Bun 使用优化的序列化路径,直接存储属性,无需完整的结构化克隆。
简单对象快速路径适用条件:
- 是普通对象且没有修改原型链
- 只包含可枚举、可配置的数据属性
- 没有索引属性或 getter/setter 方法
- 所有属性值都是原始类型或字符串
postMessage 性能可提升 2 到 241 倍,消息长度不再显著影响性能。
Bun(含快速路径):
message 事件处理器:
终止工作线程
当Worker 实例的事件循环没有待处理任务时,它会自动终止。在全局范围或任何 MessagePort 上附加 "message" 监听器会保持事件循环活动。要强制终止 Worker,调用 worker.terminate()。
process.exit()
工作线程可以调用 process.exit() 自行终止,但这不会终止主进程。与 Node.js 相同,process.on('beforeExit', callback) 和 process.on('exit', callback) 会在工作线程中触发(不会在主线程触发),退出代码会通过 "close" 事件传递。
"close" 事件
当 worker 被终止时,会发出 "close" 事件。worker 实际终止可能需要时间,因此当 worker 被标记为已终止时会触发该事件。CloseEvent 包含传递给 process.exit() 的退出代码,如果因其他原因关闭则为 0。
生命周期管理
默认情况下,活跃的Worker 会保持主进程运行,因此异步任务如 setTimeout 和 Promise 会让进程持续运行。附加 message 监听器也会保持 worker 活跃。
worker.unref()
调用 worker.unref() 可使 worker 不再阻止进程退出。这会将 worker 的生命周期与主进程解耦,相当于 Node.js 的 worker_threads 行为。
worker.unref() 在浏览器中不可用。
worker.ref()
调用 worker.ref() 可以保持进程运行直到 Worker 终止。带引用的 worker 是默认行为,仍需要事件循环中有活动(如 "message" 监听器)来保持运行。
Worker 传入 options 对象来设置:
worker.ref() 在浏览器中不可用。
使用 smol 模式降低内存使用
JavaScript 实例可能消耗大量内存。Bun 的 Worker 支持 smol 模式,可通过牺牲性能来降低内存占用。要启用 smol 模式,向 Worker 构造函数的 options 对象传入 smol: true。
`smol` 模式究竟做了什么?
`smol` 模式究竟做了什么?
设置
smol: true 会将 JSC::HeapSize 设置为 Small,而非默认的 Large。环境数据共享
使用setEnvironmentData() 和 getEnvironmentData() 在主线程和工作线程间共享数据。
Worker 事件
使用process.emit() 监听 worker 创建事件:
Bun.isMainThread
你可以通过检测 Bun.isMainThread 来判断代码是否运行在主线程。