From harness-claude
Offloads CPU-intensive work to worker threads using MessageChannel, SharedArrayBuffer, and worker pools to avoid blocking the Node.js event loop.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness-claude:node-worker-threadsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Offload CPU-intensive work to worker threads using MessageChannel and shared buffers
Offload CPU-intensive work to worker threads using MessageChannel and shared buffers
// main.ts
import { Worker } from 'node:worker_threads';
const worker = new Worker('./worker.ts', {
workerData: { input: [1, 2, 3, 4, 5] },
});
worker.on('message', (result) => {
console.log('Result:', result);
});
worker.on('error', (err) => {
console.error('Worker error:', err);
});
worker.on('exit', (code) => {
if (code !== 0) console.error(`Worker exited with code ${code}`);
});
// worker.ts
import { parentPort, workerData } from 'node:worker_threads';
const result = workerData.input.map((n: number) => n * n);
parentPort?.postMessage(result);
function runWorker<T>(workerPath: string, data: unknown): Promise<T> {
return new Promise((resolve, reject) => {
const worker = new Worker(workerPath, { workerData: data });
worker.on('message', resolve);
worker.on('error', reject);
worker.on('exit', (code) => {
if (code !== 0) reject(new Error(`Worker exited with code ${code}`));
});
});
}
const result = await runWorker<number[]>('./worker.ts', { input: data });
import { Worker } from 'node:worker_threads';
import os from 'node:os';
class WorkerPool {
private workers: Worker[] = [];
private queue: Array<{ data: unknown; resolve: Function; reject: Function }> = [];
private available: Worker[] = [];
constructor(
private workerPath: string,
size = os.cpus().length
) {
for (let i = 0; i < size; i++) {
const worker = new Worker(workerPath);
worker.on('message', (result) => {
this.available.push(worker);
this.processQueue();
});
this.workers.push(worker);
this.available.push(worker);
}
}
exec<T>(data: unknown): Promise<T> {
return new Promise((resolve, reject) => {
this.queue.push({ data, resolve, reject });
this.processQueue();
});
}
private processQueue() {
while (this.queue.length > 0 && this.available.length > 0) {
const worker = this.available.pop()!;
const { data, resolve } = this.queue.shift()!;
worker.once('message', resolve);
worker.postMessage(data);
}
}
async destroy() {
await Promise.all(this.workers.map((w) => w.terminate()));
}
}
// Share a buffer between main thread and worker
const sharedBuffer = new SharedArrayBuffer(1024);
const sharedArray = new Int32Array(sharedBuffer);
const worker = new Worker('./worker.ts', {
workerData: { buffer: sharedBuffer },
});
// Worker can read/write sharedArray directly — no serialization
const buffer = new ArrayBuffer(1024 * 1024); // 1MB
worker.postMessage({ buffer }, [buffer]); // Transfer, not copy
// buffer is now detached in the main thread
Worker threads run JavaScript in parallel OS threads within the same Node.js process. They share memory space but have separate V8 instances and event loops.
Worker threads vs child processes:
Communication overhead: postMessage serializes data using the structured clone algorithm. For large objects, this serialization can be slower than the computation itself. Use SharedArrayBuffer or transferable objects to avoid copying.
Trade-offs:
https://nodejs.org/api/worker_threads.html
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeMoves CPU-intensive tasks off the main thread using Web Workers, Comlink for RPC-style communication, and worker pooling. Covers integration with React and bundlers.
Implements Web Workers and Node.js worker_threads in Bun for parallel processing, transferable objects, shared memory, and worker pools.
Offloads expensive browser computations to web workers (including SharedWorker, worker pools, Comlink) to maintain UI responsiveness. Covers Vite/Webpack worker integration, transferable objects, and lifecycle management.