Worker permite que você inicie e comunique com uma nova instância JavaScript rodando em uma thread separada enquanto compartilha recursos de I/O com a thread principal.
O Bun implementa uma versão mínima da Web Workers API com extensões que fazem funcionar melhor para casos de uso server-side. Como o resto do Bun, Worker no Bun suporta CommonJS, ES Modules, TypeScript, JSX, TSX e mais nativamente. Nenhum passo de build extra é necessário.
Criando um Worker
Como em browsers, Worker é um global. Use para criar uma nova thread de worker.
Da thread principal
const worker = new Worker("./worker.ts");
worker.postMessage("hello");
worker.onmessage = event => {
console.log(event.data);
};Thread do worker
// previne erros de TS
declare var self: Worker;
self.onmessage = (event: MessageEvent) => {
console.log(event.data);
postMessage("world");
};Para prevenir erros TypeScript ao usar self, adicione esta linha no topo do seu arquivo worker.
declare var self: Worker;Você pode usar sintaxe import e export no seu código worker. Ao contrário de browsers, não há necessidade de especificar {type: "module"} para usar ES Modules.
Para simplificar o tratamento de erros, o script inicial para carregar é resolvido no momento em que new Worker(url) é chamado.
const worker = new Worker("/not-found.js");
// lança um erro imediatamenteO specifier passado para Worker é resolvido relativo à root do projeto (como digitar bun ./path/to/file.js).
preload - carrega módulos antes do worker iniciar
Você pode passar um array de module specifiers para a opção preload para carregar módulos antes do worker iniciar. Isto é útil quando você quer garantir que algum código seja sempre carregado antes da aplicação iniciar, como carregar OpenTelemetry, Sentry, DataDog, etc.
const worker = new Worker("./worker.ts", {
preload: ["./load-sentry.js"],
});Como o argumento de CLI --preload, a opção preload é processada antes do worker iniciar.
Você também pode passar uma única string para a opção preload:
const worker = new Worker("./worker.ts", {
preload: "./load-sentry.js",
});URLs blob:
Você também pode passar uma URL blob: para Worker. Isto é útil para criar workers a partir de strings ou outras fontes.
const blob = new Blob([`self.onmessage = (event: MessageEvent) => postMessage(event.data)`], {
type: "application/typescript",
});
const url = URL.createObjectURL(blob);
const worker = new Worker(url);Como o resto do Bun, workers criados a partir de URLs blob: suportam TypeScript, JSX e outros tipos de arquivo nativamente. Você pode comunicar que deve ser carregado via typescript ou via type ou passando um filename para o construtor File.
const file = new File([`self.onmessage = (event: MessageEvent) => postMessage(event.data)`], "worker.ts");
const url = URL.createObjectURL(file);
const worker = new Worker(url);"open"
O evento "open" é emitido quando um worker é criado e está pronto para receber mensagens. Isto pode ser usado para enviar uma mensagem inicial para um worker uma vez que esteja pronto. (Este evento não existe em browsers.)
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
worker.addEventListener("open", () => {
console.log("worker is ready");
});Mensagens são automaticamente enfileiradas até que o worker esteja pronto, então não há necessidade de esperar pelo evento "open" para enviar mensagens.
Mensagens com postMessage
Para enviar mensagens, use worker.postMessage e self.postMessage. Isto aproveita o HTML Structured Clone Algorithm.
Otimizações de performance
O Bun inclui fast paths otimizadas para postMessage para melhorar dramaticamente a performance para tipos de dados comuns:
String fast path - Ao postar valores de string puros, o Bun ignora completamente o structured clone algorithm, alcançando ganhos de performance significativos sem overhead de serialização.
Simple object fast path - Para objetos plain contendo apenas valores primitivos (strings, números, booleanos, null, undefined), o Bun usa um caminho de serialização otimizado que armazena propriedades diretamente sem full structured cloning.
O simple object fast path ativa quando o objeto:
- É um plain object sem modificações na cadeia de prototype
- Contém apenas propriedades de dados enumerable, configurable
- Não tem propriedades indexadas ou métodos getter/setter
- Todos os valores de propriedade são primitives ou strings
Com estes fast paths, o postMessage do Bun performa 2-241x mais rápido porque o comprimento da mensagem não tem mais um impacto significativo na performance.
Bun (com fast paths):
postMessage({ prop: 11 chars string, ...9 more props }) - 648ns
postMessage({ prop: 14 KB string, ...9 more props }) - 719ns
postMessage({ prop: 3 MB string, ...9 more props }) - 1.26µsNode.js v24.6.0 (para comparação):
postMessage({ prop: 11 chars string, ...9 more props }) - 1.19µs
postMessage({ prop: 14 KB string, ...9 more props }) - 2.69µs
postMessage({ prop: 3 MB string, ...9 more props }) - 304µs// String fast path - otimizado
postMessage("Hello, worker!");
// Simple object fast path - otimizado
postMessage({
message: "Hello",
count: 42,
enabled: true,
data: null,
});
// Objetos complexos ainda funcionam mas usam structured clone standard
postMessage({
nested: { deep: { object: true } },
date: new Date(),
buffer: new ArrayBuffer(8),
});// Na thread do worker, `postMessage` é automaticamente "roteado" para a thread pai.
postMessage({ hello: "world" });
// Na thread principal
worker.postMessage({ hello: "world" });Para receber mensagens, use o message event handler na thread do worker e thread principal.
// Thread do worker:
self.addEventListener("message", event => {
console.log(event.data);
});
// ou use o setter:
// self.onmessage = fn
// se na thread principal
worker.addEventListener("message", event => {
console.log(event.data);
});
// ou use o setter:
// worker.onmessage = fnTerminando um worker
Uma instância Worker termina automaticamente uma vez que seu event loop não tem mais trabalho para fazer. Anexar um listener "message" no global ou qualquer MessagePort manterá o event loop vivo. Para terminar forçadamente um Worker, chame worker.terminate().
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
// ...algum tempo depois
worker.terminate();Isto fará com que o worker saia o mais rápido possível.
process.exit()
Um worker pode terminar a si mesmo com process.exit(). Isto não termina o processo principal. Como no Node.js, process.on('beforeExit', callback) e process.on('exit', callback) são emitidos na thread do worker (e não na thread principal), e o exit code é passado para o evento "close".
"close"
O evento "close" é emitido quando um worker foi terminado. Pode levar algum tempo para o worker realmente terminar, então este evento é emitido quando o worker foi marcado como terminado. O CloseEvent conterá o exit code passado para process.exit(), ou 0 se fechado por outros motivos.
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
worker.addEventListener("close", event => {
console.log("worker is being closed");
});Este evento não existe em browsers.
Gerenciando lifetime
Por padrão, um Worker ativo manterá o processo principal (spawning) vivo, então tarefas async como setTimeout e promises manterão o processo vivo. Anexar listeners message também manterá o Worker vivo.
worker.unref()
Para impedir que um worker em execução mantenha o processo vivo, chame worker.unref(). Isto desacopla o lifetime do worker do lifetime do processo principal e é equivalente ao que worker_threads do Node.js faz.
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
worker.unref();Nota: worker.unref() não está disponível em browsers.
worker.ref()
Para manter o processo vivo até o Worker terminar, chame worker.ref(). Um worker ref'd é o comportamento padrão e ainda precisa de algo acontecendo no event loop (como um listener "message") para o worker continuar rodando.
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
worker.unref();
// depois...
worker.ref();Alternativamente, você também pode passar um objeto options para Worker:
const worker = new Worker(new URL("worker.ts", import.meta.url).href, {
ref: false,
});Nota: worker.ref() não está disponível em browsers.
Uso de memória com smol
Instâncias JavaScript podem usar muita memória. O Worker do Bun suporta um modo smol que reduz uso de memória, ao custo de performance. Para habilitar o modo smol, passe smol: true para o objeto options no construtor Worker.
const worker = new Worker("./i-am-smol.ts", {
smol: true,
});O que o modo smol realmente faz?">
Definir smol: true define JSC::HeapSize como Small em vez do padrão Large.
Environment Data
Compartilhe dados entre a thread principal e workers usando setEnvironmentData() e getEnvironmentData().
import { setEnvironmentData, getEnvironmentData } from "worker_threads";
// Na thread principal
setEnvironmentData("config", { apiUrl: "https://api.example.com" });
// No worker
const config = getEnvironmentData("config");
console.log(config); // => { apiUrl: "https://api.example.com" }Worker Events
Ouça eventos de criação de worker usando process.emit():
process.on("worker", worker => {
console.log("New worker created:", worker.threadId);
});Bun.isMainThread
Você pode verificar se está na thread principal verificando Bun.isMainThread.
if (Bun.isMainThread) {
console.log("I'm the main thread");
} else {
console.log("I'm in a worker");
}Isto é útil para rodar código condicionalmente baseado em se você está na thread principal ou não.