🚧 — Worker
API 仍在實驗階段,不應視為已準備好用於生產環境。
Worker
讓您啟動新的 JavaScript 實例並與之通訊,該實例在獨立的執行緒上執行,同時與主執行緒共享 I/O 資源。
Bun 實作了最小版本的 Web Workers API,並擴展了其功能,使其更適用於伺服器端的使用情境。如同 Bun 的其他部分,Bun 中的 Worker
支援 CommonJS、ES Modules、TypeScript、JSX、TSX 等,開箱即用。無需額外的建置步驟。
建立 Worker
就像在瀏覽器中一樣,Worker
是全域的。使用它來建立新的工作執行緒。
從主執行緒
const worker = new Worker("./worker.ts");
worker.postMessage("hello");
worker.onmessage = event => {
console.log(event.data);
};
工作執行緒
// prevents TS errors
declare var self: Worker;
self.onmessage = (event: MessageEvent) => {
console.log(event.data);
postMessage("world");
};
為了避免在使用 self
時發生 TypeScript 錯誤,請將此行新增至您的 worker 檔案頂部。
declare var self: Worker;
您可以在您的 worker 程式碼中使用 import
和 export
語法。與瀏覽器不同,無需指定 {type: "module"}
即可使用 ES Modules。
為了簡化錯誤處理,要載入的初始腳本會在呼叫 new Worker(url)
時解析。
const worker = new Worker("/not-found.js");
// throws an error immediately
傳遞給 Worker
的指定符會相對於專案根目錄解析(如同輸入 bun ./path/to/file.js
)。
preload
- 在工作執行緒啟動前載入模組
您可以將模組指定符陣列傳遞給 preload
選項,以便在工作執行緒啟動前載入模組。當您想要確保某些程式碼總是在應用程式啟動前載入時,這非常有用,例如載入 OpenTelemetry、Sentry、DataDog 等。
const worker = new Worker("./worker.ts", {
preload: ["./load-sentry.js"],
});
就像 --preload
CLI 參數一樣,preload
選項會在工作執行緒啟動前處理。
您也可以將單一字串傳遞給 preload
選項
const worker = new Worker("./worker.ts", {
preload: "./load-sentry.js",
});
此功能在 Bun v1.1.35 中新增。
blob:
URL
從 Bun v1.1.13 開始,您也可以將 blob:
URL 傳遞給 Worker
。這對於從字串或其他來源建立工作執行緒非常有用。
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 建立的工作執行緒支援 TypeScript、JSX 和其他檔案類型,開箱即用。您可以透過 type
或將 filename
傳遞給 File
建構函式來溝通應以 typescript 載入。
const file = new File(
[
`
self.onmessage = (event: MessageEvent) => postMessage(event.data)`,
],
"worker.ts",
);
const url = URL.createObjectURL(file);
const worker = new Worker(url);
"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 結構化複製演算法。
// 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()
。已 ref 的工作執行緒是預設行為,並且仍然需要在事件迴圈中進行某些操作(例如 "message"
監聽器),工作執行緒才能繼續執行。
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
worker.unref();
// later...
worker.ref();
或者,您也可以將 options
物件傳遞給 Worker
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
模式實際上做了什麼?
Bun.isMainThread
您可以透過檢查 Bun.isMainThread
來檢查您是否在主執行緒中。
if (Bun.isMainThread) {
console.log("I'm the main thread");
} else {
console.log("I'm in a worker");
}
這對於根據您是否在主執行緒中條件式地執行程式碼非常有用。