Bun

工作人員

🚧Worker API 仍處於實驗階段,不應視為已準備好投入生產。

Worker 讓您可以在與主執行緒共用 I/O 資源的情況下,啟動並與在獨立執行緒上執行的全新 JavaScript 執行個體進行通訊。

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 執行緒的事件迴圈沒有工作可做時,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");
});

瀏覽器中不存在此事件。

管理生命週期

預設情況下,一個 active 的 Worker 會讓主程式 (spawning) 保持運作,因此非同步任務,例如 setTimeout 和 promises,會讓程式保持運作。附加 message 監聽器也會讓 Worker 保持運作。

worker.unref()

若要停止正在執行的 worker 讓程式保持運作,請呼叫 worker.unref()。這會將 worker 的生命週期與主程式的生命週期分開,等同於 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'd worker 是預設行為,且仍需要在事件迴圈中進行某些動作 (例如 "message" 監聽器),才能讓 worker 繼續執行。

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 模式實際上做了什麼?