How to Implement Worker Threads in Bun for Parallel Processing: Complete Guide with Examples
Bun implements a Node.js-compatible worker_threads module that spawns isolated JavaScript VMs on separate OS threads, enabling parallel computation through message passing while leveraging Bun's high-performance Web Worker foundation.
Bun's implementation of Worker threads allows developers to utilize multi-core processors for CPU-intensive tasks without leaving the JavaScript ecosystem. Located in the oven-sh/bun repository, the worker_threads module at src/js/node/worker_threads.ts provides full API compatibility with Node.js while offering additional performance optimizations specific to Bun's JavaScriptCore engine.
Architecture of Bun’s Worker Thread Implementation
The worker_threads module in Bun is implemented entirely in TypeScript and serves as a thin wrapper around Bun's native Web Worker implementation. This architectural choice enables Node.js compatibility while maintaining the performance characteristics of Bun's JavaScriptCore runtime.
The Worker Class Structure
The Worker class extends EventEmitter and encapsulates a native Web Worker instance (referenced as the private field #worker). According to lines 26-88 of src/js/node/worker_threads.ts, this class translates Node-style events (message, error, close, online) into the corresponding Web Worker lifecycle events. The constructor (lines 52-73) handles script loading through either file paths or inline evaluation via temporary Blob URLs when the eval option is enabled.
Message Port Implementation
Bun creates a fake MessagePort for parentPort using the fakeParentPort() function (lines 30-44). This wrapper forwards postMessage and onmessage calls to the global self object within the worker thread, ensuring that code written for Node.js worker_threads functions identically in Bun without modification. This implementation activates automatically when the file detects it is not running on the main thread.
Native Bindings and Thread State
Thread-local data including workerData, threadId, and environment mappings are injected from the native side via $cpp("Worker.cpp","createNodeWorkerThreadsBinding") (lines 22-33). This C++ bridge connects the JavaScript API to Bun's underlying thread management system, allowing seamless access to thread-specific information from JavaScript.
Creating Worker Threads in Bun
Basic Worker Creation and Messaging
To spawn a worker, import the Worker class from node:worker_threads and instantiate it with a file path. The following example demonstrates bidirectional communication between the main thread and a worker:
// main.ts
import { Worker } from "node:worker_threads";
const worker = new Worker("./worker.ts");
worker.on("message", (msg) => console.log("Main received:", msg));
worker.postMessage({ task: "heavy computation" });
// worker.ts
import { parentPort } from "node:worker_threads";
parentPort?.on("message", (data) => {
const result = data.task.split("").reverse().join("");
parentPort?.postMessage({ result });
});
Accessing Thread Context with workerData
The workerData property allows you to pass initialization data to worker threads during construction. Combined with isMainThread, you can create self-contained modules that function as both entry points and workers:
// compute.ts
import { Worker, isMainThread, workerData } from "node:worker_threads";
if (isMainThread) {
// Main thread: spawn workers
for (let i = 0; i < 4; i++) {
new Worker(import.meta.url, {
workerData: { workerId: i, start: i * 1000, end: (i + 1) * 1000 }
});
}
} else {
// Worker thread: access initialized data
console.log(`Worker ${workerData.workerId} processing range ${workerData.start}-${workerData.end}`);
// Perform computation...
}
Sharing Environment Data Across Threads
Use setEnvironmentData and getEnvironmentData to share configuration objects between the main thread and all spawned workers without passing them through workerData for each instance:
// main.ts
import { setEnvironmentData, Worker } from "node:worker_threads";
setEnvironmentData("databaseConfig", { host: "localhost", port: 5432 });
new Worker("./db-worker.ts");
// db-worker.ts
import { getEnvironmentData } from "node:worker_threads";
const config = getEnvironmentData("databaseConfig");
console.log("Connecting to:", config.host);
Worker Lifecycle and Performance Optimization
Termination and Resource Management
Control worker lifetimes using terminate(), unref(), and ref(). The terminate() method returns a Promise<number> that resolves with the worker's exit code (implemented in lines 15-22 of the source):
const worker = new Worker("./long-task.ts");
worker.unref(); // Allow process to exit even if worker is running
// Force termination after timeout
setTimeout(async () => {
const exitCode = await worker.terminate();
console.log(`Worker exited with code: ${exitCode}`);
}, 5000);
Memory Optimization with smol Mode
For applications spawning many lightweight workers, enable the smol option to reduce per-worker heap size, and use preload to inject instrumentation or polyfills before the worker script executes:
const worker = new Worker("./task.ts", {
smol: true, // Reduce memory footprint
preload: ["./tracing.js"] // Load monitoring before main script
});
Message Passing Performance
Bun optimizes postMessage for plain strings and simple objects by bypassing the full Structured Clone algorithm when possible. This fast-path optimization, documented in docs/runtime/workers.mdx, makes inter-thread communication significantly cheaper than standard Node.js implementations while maintaining spec compliance.
Summary
- Bun's
worker_threadsmodule resides insrc/js/node/worker_threads.tsand wraps native Web Workers to provide Node.js API compatibility. - The
Workerclass extendsEventEmitterand manages OS thread lifecycle through native bindings toWorker.cpp. - parentPort is implemented via
fakeParentPort()(lines 30-44), forwarding messages to the globalselfobject within the worker context. - Thread-local state (
workerData,threadId) is injected through C++ bindings atcreateNodeWorkerThreadsBinding(lines 22-33). - Performance optimizations include fast-path message serialization and the
smolmode for memory-constrained environments. - Use
unref()/ref()to control process lifetime dependencies, andterminate()to force shutdown with exit code retrieval.
Frequently Asked Questions
Are Bun worker threads compatible with existing Node.js code?
Yes. Bun's implementation of worker_threads in src/js/node/worker_threads.ts provides full API compatibility with Node.js, including Worker, parentPort, workerData, isMainThread, MessageChannel, BroadcastChannel, and environment data methods. You can migrate existing Node.js worker code to Bun without modifications to the public API surface.
How does Bun's Worker implementation differ from Node.js?
While Node.js builds workers on top of libuv and V8 isolates, Bun implements workers as a TypeScript wrapper around its JavaScriptCore-based Web Worker engine defined in src/js/node/worker_threads.ts. This architecture allows Bun to optimize message passing through fast-path serialization for simple objects and provides additional options like smol mode for reduced memory usage per thread.
Can I share memory between worker threads in Bun?
Yes. Bun supports SharedArrayBuffer and the standard MessageChannel API for zero-copy data sharing between threads. The workerData mechanism also allows passing structured cloneable data during worker initialization via the native binding layer, while setEnvironmentData enables process-wide configuration sharing.
What happens when I call worker.terminate() in Bun?
The terminate() method immediately stops the worker thread and returns a Promise<number> that resolves with the worker's exit code (implementation lines 15-22). This differs from unref(), which merely allows the process to exit naturally without forcing the worker to stop execution or resolving with an exit status.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →