🚧 —
WorkerAPI 仍处于实验阶段,不应视为生产就绪。
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);
};
工作线程
// 防止TS错误
declare var self: Worker;
self.onmessage = (event: MessageEvent) => {
console.log(event.data);
postMessage("world");
};
为了在使用self时防止 TypeScript 错误,请在工作文件的顶部添加这一行。
declare var self: Worker;
您可以在工作代码中使用import和export语法。与浏览器不同,无需指定{type: "module"}以使用 ES 模块。
为了简化错误处理,在调用new Worker(url)时,初始脚本将在解析时加载。
const worker = new Worker("/not-found.js");
// 立即引发错误
传递给Worker的 specifier 是相对于项目根目录解析的(就像输入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.postMessage 和 self.postMessage。这利用了HTML 结构化克隆算法。
// 在工作线程上,`postMessage`会自动“路由”到父线程。
postMessage({ hello: "world" });
// 在主线程上
worker.postMessage({ hello: "world" });
要接收消息,使用工作线程和主线程上的message事件处理程序。
// 工作线程:
self.addEventListener("message", (event) => {
console.log(event.data);
});
// 或使用setter:
// self.onmessage = fn
// 如果在主线程上
worker.addEventListener("message", (event) => {
console.log(event.data);
});
// 或使用setter:
// worker.onmessage = fn
终止工作线程
Worker实例在其事件循环没有剩余工作时会自动终止。将全局或任何MessagePort上附加"message"监听器将使事件循环保持活动状态。要强制终止Worker,请调用worker.terminate()。
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
// ...稍后
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和 promises)将使进程保持活动状态。附加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()。引用的工作线程是默认行为,仍然需要事件
循环中发生的某些事情(例如"message"监听器)以使工作线程继续运行。
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
worker.unref();
// 稍后...
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模式,请在Worker构造函数的options对象中传递smol: true。
const worker = new Worker("./i-am-smol.ts", {
smol: true,
});