Bun

工作进程

🚧Worker API 仍处于实验阶段,不应将其视为已准备好投入生产。

Worker 允许您启动一个新的 JavaScript 实例,该实例在单独的线程上运行,同时与主线程共享 I/O 资源,并与其进行通信。

Bun 实现了一个极简版本的 Web Workers API,并进行了扩展,使其更适合于服务器端用例。与 Bun 的其他部分一样,Bun 中的 Worker 开箱即用地支持 CommonJS、ES 模块、TypeScript、JSX、TSX 等。无需额外的构建步骤。

创建 Worker

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

从主线程

主线程
const workerURL = new URL("worker.ts", import.meta.url).href;
const worker = new Worker(workerURL);

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

工作进程线程

worker.ts(工作进程线程)
// prevents TS errors
declare var self: Worker;

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

要在使用 self 时防止出现 TypeScript 错误,请将此行添加到工作进程文件顶部。

declare var self: Worker;

您可以在工作进程代码中使用 importexport 语法。与浏览器不同,无需指定 {type: "module"} 即可使用 ES 模块。

为了简化错误处理,加载的初始脚本在调用 new Worker(url) 时得到解析。

const worker = new Worker("/not-found.js");
// throws an error immediately

传递给 Worker 的说明符相对于项目根目录进行解析(例如键入 bun ./path/to/file.js)。

"open"

当创建一个工作进程并准备好接收消息时,将发出 "open" 事件。这可用于在工作进程准备就绪后向其发送初始消息。(浏览器中不存在此事件。)

const worker = new Worker(new URL("worker.ts", import.meta.url).href);

worker.addEventListener("open", () => {
  console.log("worker is ready");
});

消息会自动排队,直到工作进程准备就绪,因此无需等待 "open" 事件即可发送消息。

使用 postMessage 的消息

要发送消息,请使用 worker.postMessageself.postMessage。这利用了 HTML 结构化克隆算法

// On the worker thread, `postMessage` is automatically "routed" to the parent thread.
postMessage({ hello: "world" });

// On the main thread
worker.postMessage({ hello: "world" });

要接收消息,请在工作进程和主线程上使用 message 事件处理程序

// Worker thread:
self.addEventListener("message", event => {
  console.log(event.data);
});
// or use the setter:
// self.onmessage = fn

// if on the main thread
worker.addEventListener("message", event => {
  console.log(event.data);
});
// or use the setter:
// worker.onmessage = fn

终止工作进程

Worker 实例的事件循环没有剩余工作时,它将自动终止。在全局或任何 MessagePort 上附加 "message" 侦听器将使事件循环保持活动状态。要强制终止 Worker,请调用 worker.terminate()

const worker = new Worker(new URL("worker.ts", import.meta.url).href);

// ...some time later
worker.terminate();

这将导致工作进程尽快退出。

process.exit()

工作进程可以使用 process.exit() 自行终止。这不会终止主进程。与 Node.js 中一样,process.on('beforeExit', callback)process.on('exit', callback) 在工作进程线程(而不是主线程)上发出,并且退出代码传递给 "close" 事件。

"close"

当工作进程终止时,将发出 "close" 事件。工作进程实际终止可能需要一些时间,因此当工作进程被标记为已终止时,将发出此事件。CloseEvent 将包含传递给 process.exit() 的退出代码,或者如果由于其他原因关闭,则为 0。

const worker = new Worker(new URL("worker.ts", import.meta.url).href);

worker.addEventListener("close", event => {
  console.log("worker is being closed");
});

浏览器中不存在此事件。

管理生命周期

默认情况下,活动 Worker 将使主(生成)进程保持活动状态,因此异步任务(如 setTimeout 和 Promise)将使进程保持活动状态。附加 message 侦听器也将使 Worker 保持活动状态。

worker.unref()

要阻止正在运行的工作进程使进程保持活动状态,请调用 worker.unref()。这将使工作进程的生命周期与主进程的生命周期分离,并且等同于 Node.js 的 worker_threads 所做的操作。

const worker = new Worker(new URL("worker.ts", import.meta.url).href);
worker.unref();

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

worker.ref()

要让进程保持活动状态,直到 Worker 终止,请调用 worker.ref()。引用 worker 是默认行为,并且仍需要在事件循环中进行一些操作(例如 "message" 侦听器)才能让 worker 继续运行。

const worker = new Worker(new URL("worker.ts", import.meta.url).href);
worker.unref();
// later...
worker.ref();

或者,你还可以向 Worker 传递一个 options 对象

const worker = new Worker(new URL("worker.ts", import.meta.url).href, {
  ref: false,
});

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

使用 smol 的内存使用情况

JavaScript 实例可以使用大量内存。Bun 的 Worker 支持 smol 模式,该模式以牺牲性能为代价来减少内存使用。要启用 smol 模式,请将 smol: true 传递给 Worker 构造函数中的 options 对象。

const worker = new Worker("./i-am-smol.ts", {
  smol: true,
});

smol 模式实际上做了什么?