> ## Documentation Index
> Fetch the complete documentation index at: https://bun.zhcndoc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Workers

> 使用 Bun 的 Workers API 创建并与一个在独立线程上运行、但与主线程共享 I/O 资源的新 JavaScript 实例进行通信

<Warning>
  `Worker` API 仍处于实验阶段（尤其是终止 workers 功能）。我们正在积极改进这一点。
</Warning>

[`Worker`](https://developer.mozilla.org/en-US/docs/Web/API/Worker) 允许你启动并与一个运行在独立线程上的新的 JavaScript 实例通信，同时与主线程共享 I/O 资源。

Bun 实现了 [Web Workers API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) 的一个简化版本，同时添加了扩展，使其更适合服务器端的使用场景。与 Bun 的其他部分一样，Bun 中的 `Worker` 原生支持 CommonJS、ES 模块、TypeScript、JSX、TSX 等，无需额外构建步骤。

## 创建一个 `Worker`

与浏览器中一样，[`Worker`](https://developer.mozilla.org/en-US/docs/Web/API/Worker) 是全局对象。使用它可以创建一个新的工作线程。

### 在主线程中

```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const worker = new Worker("./worker.ts");

worker.postMessage("hello");
worker.onmessage = event => {
  console.log(event.data);
};
```

### 工作线程

```ts worker.ts icon="file-code" theme={"theme":{"light":"github-light","dark":"dracula"}}
// 防止 TypeScript 报错
declare var self: Worker;

self.onmessage = (event: MessageEvent) => {
  console.log(event.data);
  postMessage("world");
};
```

为防止在使用 `self` 时出现 TypeScript 报错，请在工作线程文件顶部添加以下声明：

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
declare var self: Worker;
```

你可以在工作线程代码中使用 `import` 和 `export` 语法。与浏览器不同的是，不需要指定 `{type: "module"}` 来使用 ES 模块。

为了简化错误处理，初始脚本会在调用 `new Worker(url)` 时立即解析。

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
const worker = new Worker("/not-found.js");
// 会立即抛出错误
```

传递给 `Worker` 的路径会相对于项目根目录解析（类似于在命令行运行 `bun ./path/to/file.js`）。

### `preload` - 在 worker 启动前加载模块

你可以通过 `preload` 选项传递一个模块路径数组，以便在 worker 启动前加载这些模块。这在你想确保某些代码（如加载 OpenTelemetry、Sentry、DataDog 等）总是在应用启动前加载时非常有用。

```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const worker = new Worker("./worker.ts", {
  preload: ["./load-sentry.js"],
});
```

和 `--preload` CLI 参数一样，`preload` 选项会在 worker 启动前处理。

你也可以向 `preload` 选项传入单个字符串：

```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const worker = new Worker("./worker.ts", {
  preload: "./load-sentry.js",
});
```

### `blob:` URL

你也可以向 `Worker` 传入 `blob:` URL。这对于从字符串或其他来源创建 worker 很有用。

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
const blob = new Blob([`self.onmessage = (event: MessageEvent) => postMessage(event.data)`], {
  type: "application/typescript",
});
const url = URL.createObjectURL(blob);
const worker = new Worker(url);
```

和 Bun 的其他部分一样，从 `blob:` URL 创建的 workers 也默认支持 TypeScript、JSX 等文件类型。你可以通过设置 `type` 或在 `File` 构造函数里传入文件名来告诉它使用 TypeScript。

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
const file = new File([`self.onmessage = (event: MessageEvent) => postMessage(event.data)`], "worker.ts");
const url = URL.createObjectURL(file);
const worker = new Worker(url);
```

### `"open"` 事件

当 worker 创建完成并准备好接收消息时，会发出 `"open"` 事件。你可以使用该事件向 worker 发送初始消息。（此事件在浏览器中不存在）

```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const worker = new Worker(new URL("worker.ts", import.meta.url).href);

worker.addEventListener("open", () => {
  console.log("worker 已准备好");
});
```

消息会自动排队等待发送，直到 worker 准备就绪，因此你无需等待 `"open"` 事件即可发送消息。

## 使用 `postMessage` 发送消息

要发送消息，使用 [`worker.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage) 和 [`self.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)。这利用了 [HTML 结构化克隆算法](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm)。

### 性能优化

Bun 为 `postMessage` 包含了优化的快速路径，以显著提升常用数据类型的性能：

**字符串快速路径** - 当只发送纯字符串时，Bun 直接绕过结构化克隆算法，显著减少序列化开销。

**简单对象快速路径** - 对于仅包含原始值（字符串、数字、布尔值、null 和 undefined）的普通对象，Bun 使用优化的序列化路径，直接存储属性，无需完整的结构化克隆。

简单对象快速路径适用条件：

* 是普通对象且没有修改原型链
* 只包含可枚举、可配置的数据属性
* 没有索引属性或 getter/setter 方法
* 所有属性值都是原始类型或字符串

有了这些快速路径，Bun 的 `postMessage` 性能可提升 **2 到 241 倍**，消息长度不再显著影响性能。

**Bun（含快速路径）：**

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
postMessage({ prop: 11 字符串, ...9 个其他属性 }) - 648 纳秒
postMessage({ prop: 14 KB 字符串, ...9 个其他属性 }) - 719 纳秒
postMessage({ prop: 3 MB 字符串, ...9 个其他属性 }) - 1.26 微秒
```

**Node.js v24.6.0（对比）：**

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
postMessage({ prop: 11 字符串, ...9 个其他属性 }) - 1.19 微秒
postMessage({ prop: 14 KB 字符串, ...9 个其他属性 }) - 2.69 微秒
postMessage({ prop: 3 MB 字符串, ...9 个其他属性 }) - 304 微秒
```

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
// 字符串快速路径 - 优化处理
postMessage("Hello, worker!");

// 简单对象快速路径 - 优化处理
postMessage({
  message: "Hello",
  count: 42,
  enabled: true,
  data: null,
});

// 复杂对象仍然可用，但会使用标准结构化克隆
postMessage({
  nested: { deep: { object: true } },
  date: new Date(),
  buffer: new ArrayBuffer(8),
});
```

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
// 在工作线程中，`postMessage` 自动“路由”至父线程。
postMessage({ hello: "world" });

// 在主线程中
worker.postMessage({ hello: "world" });
```

接收消息可使用 worker 或主线程的 [`message` 事件处理器](https://developer.mozilla.org/en-US/docs/Web/API/Worker/message_event)：

```js theme={"theme":{"light":"github-light","dark":"dracula"}}
// 工作线程：
self.addEventListener("message", event => {
  console.log(event.data);
});
// 或者使用简写：
// self.onmessage = fn

// 如果在主线程：
worker.addEventListener("message", event => {
  console.log(event.data);
});
// 或者使用简写：
// worker.onmessage = fn
```

## 终止工作线程

当 `Worker` 实例的事件循环没有待处理任务时，它会自动终止。在全局范围或任何 `MessagePort` 上附加 `"message"` 监听器会保持事件循环活动。要强制终止 `Worker`，调用 `worker.terminate()`。

```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const worker = new Worker(new URL("worker.ts", import.meta.url).href);

// ...一段时间后
worker.terminate();
```

这会让 worker 尽快退出。

### `process.exit()`

工作线程可以调用 `process.exit()` 自行终止，但这不会终止主进程。与 Node.js 相同，`process.on('beforeExit', callback)` 和 `process.on('exit', callback)` 会在工作线程中触发（不会在主线程触发），退出代码会通过 `"close"` 事件传递。

### `"close"` 事件

当 worker 被终止时，会发出 `"close"` 事件。worker 实际终止可能需要时间，因此当 worker 被标记为已终止时会触发该事件。`CloseEvent` 包含传递给 `process.exit()` 的退出代码，如果因其他原因关闭则为 0。

```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const worker = new Worker(new URL("worker.ts", import.meta.url).href);

worker.addEventListener("close", event => {
  console.log("worker 正在关闭");
});
```

此事件在浏览器中不存在。

## 生命周期管理

默认情况下，活跃的 `Worker` 会保持主进程运行，因此异步任务如 `setTimeout` 和 Promise 会让进程持续运行。附加 `message` 监听器也会保持 worker 活跃。

### `worker.unref()`

调用 `worker.unref()` 可使 worker 不再阻止进程退出。这会将 worker 的生命周期与主进程解耦，相当于 Node.js 的 `worker_threads` 行为。

```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
worker.unref();
```

注意：`worker.unref()` 在浏览器中不可用。

### `worker.ref()`

调用 `worker.ref()` 可以保持进程运行直到 `Worker` 终止。带引用的 worker 是默认行为，仍需要事件循环中有活动（如 `"message"` 监听器）来保持运行。

```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
worker.unref();
// 以后...
worker.ref();
```

你也可以通过向 `Worker` 传入 `options` 对象来设置：

```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const worker = new Worker(new URL("worker.ts", import.meta.url).href, {
  ref: false,
});
```

注意：`worker.ref()` 在浏览器中不可用。

## 使用 `smol` 模式降低内存使用

JavaScript 实例可能消耗大量内存。Bun 的 `Worker` 支持 `smol` 模式，可通过牺牲性能来降低内存占用。要启用 `smol` 模式，向 `Worker` 构造函数的 `options` 对象传入 `smol: true`。

```ts index.ts icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
const worker = new Worker("./i-am-smol.ts", {
  smol: true,
});
```

<Accordion title="`smol` 模式究竟做了什么？">
  设置 `smol: true` 会将 `JSC::HeapSize` 设置为 `Small`，而非默认的 `Large`。
</Accordion>

## 环境数据共享

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

```ts title="index.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
import { setEnvironmentData, getEnvironmentData } from "worker_threads";

// 在主线程
setEnvironmentData("config", { apiUrl: "https://api.example.com" });

// 在工作线程
const config = getEnvironmentData("config");
console.log(config); // => { apiUrl: "https://api.example.com" }
```

## Worker 事件

使用 `process.emit()` 监听 worker 创建事件：

```ts title="index.ts" icon="https://mintcdn.com/bun-zhcndoc/cnUTwgMuf4cCrwC-/icons/typescript.svg?fit=max&auto=format&n=cnUTwgMuf4cCrwC-&q=85&s=e7767043c9e885c34f2d6c8fe2a95217" theme={"theme":{"light":"github-light","dark":"dracula"}}
process.on("worker", worker => {
  console.log("新 worker 已创建:", worker.threadId);
});
```

## `Bun.isMainThread`

你可以通过检测 `Bun.isMainThread` 来判断代码是否运行在主线程。

```ts theme={"theme":{"light":"github-light","dark":"dracula"}}
if (Bun.isMainThread) {
  console.log("我是主线程");
} else {
  console.log("我在 worker 中");
}
```

这对于基于当前线程类型有条件地运行代码非常有用。
