Skip to content

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

ts
const worker = new Worker("./worker.ts");

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

Thread do worker

ts
// 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.

ts
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.

js
const worker = new Worker("/not-found.js");
// lança um erro imediatamente

O 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.

ts
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:

ts
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.

js
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.

ts
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.)

ts
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):

ts
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µs

Node.js v24.6.0 (para comparação):

js
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
js
// 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),
});
js
// 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.

js
// 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 = fn

Terminando 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().

ts
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.

ts
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.

ts
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.

ts
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:

ts
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.

ts
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().

index.ts
ts
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():

index.ts
ts
process.on("worker", worker => {
  console.log("New worker created:", worker.threadId);
});

Bun.isMainThread

Você pode verificar se está na thread principal verificando Bun.isMainThread.

ts
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.

Bun by www.bunjs.com.cn edit