A TypeScript decorator that executes class methods in separate Node.js worker threads
npm install node-thread-decorator[![node version][node-image]][node-url]


[node-image]: https://img.shields.io/badge/node.js-%3E=_18.0-green.svg?style=flat-square
[node-url]: http://nodejs.org/download/
A TypeScript decorator that executes class methods in separate Node.js worker threads, preventing CPU-intensive operations from blocking the main event loop.
- Installation
- Quick Start
- API Reference
- Usage Examples
- Important Limitations
- Using External Dependencies
- Best Practices
- License
``bash`
npm install node-thread-decorator
`typescript
import { RunInNewThread } from 'node-thread-decorator';
class Calculator {
@RunInNewThread()
async heavyComputation(iterations: number): Promise
let result = 0;
for (let i = 0; i < iterations; i++) {
result += Math.sqrt(i) * Math.sin(i);
}
return result;
}
}
const calc = new Calculator();
const result = await calc.heavyComputation(10000000);
`
A method decorator that executes the decorated method in a separate worker thread.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| timeout | number | No | Maximum execution time in milliseconds. If exceeded, the worker is terminated and an error is thrown. |
Returns: Promise - The decorated method always returns a Promise, even if the original method was synchronous.
`typescript
import { RunInNewThread } from 'node-thread-decorator';
class MyService {
@RunInNewThread()
async processData(data: number[]): Promise
return data.reduce((sum, n) => sum + n, 0);
}
}
`
`typescript`
class MyService {
@RunInNewThread(5000) // 5 second timeout
async longRunningTask(): Promise
// If this takes more than 5 seconds, it will throw an error
return 'completed';
}
}
`typescript
import { Controller, Get } from '@nestjs/common';
import { RunInNewThread } from 'node-thread-decorator';
@Controller()
export class HealthController {
@RunInNewThread()
heavyOperation(milliseconds: number): void {
const start = Date.now();
while (Date.now() - start < milliseconds) {
// CPU-intensive work
}
}
@Get('/health')
async getHealth(): Promise
await this.heavyOperation(10000); // Won't block the main thread
return 'OK';
}
}
`
The decorator only works with class methods. Standalone functions are not supported.
`typescript
// ✅ Supported - Class method
class MyClass {
@RunInNewThread()
async myMethod() { / ... / }
}
// ❌ Not Supported - Standalone function
@RunInNewThread() // This won't work
function myFunction() { / ... / }
`
Methods executed in worker threads cannot access variables, imports, or closures from the original scope. Each worker runs in an isolated context.
`typescript
import { someHelper } from './helpers';
const CONFIG = { maxRetries: 3 };
class MyService {
@RunInNewThread()
async process(): Promise
// ❌ This will fail - 'someHelper' is not defined in worker
someHelper();
// ❌ This will fail - 'CONFIG' is not defined in worker
console.log(CONFIG.maxRetries);
}
}
`
The this keyword inside decorated methods does not refer to the class instance. Instance properties and other methods are not accessible.
`typescript
class MyService {
private value = 42;
@RunInNewThread()
async getValue(): Promise
// ❌ This will fail - 'this.value' is undefined in worker
return this.value;
}
@RunInNewThread()
async callOther(): Promise
// ❌ This will fail - 'this.otherMethod' is not a function in worker
this.otherMethod();
}
otherMethod() { / ... / }
}
`
All arguments passed to decorated methods must be serializable (transferable via postMessage). Functions, class instances, and circular references cannot be passed.
`typescript
class MyService {
@RunInNewThread()
async process(data: unknown): Promise
// ...
}
}
// ✅ Supported types
await service.process({ name: 'John', age: 30 }); // Plain objects
await service.process([1, 2, 3]); // Arrays
await service.process('hello'); // Primitives
await service.process(null); // null/undefined
// ❌ Not supported
await service.process(() => {}); // Functions
await service.process(new MyClass()); // Class instances
await service.process(circularRef); // Circular references
`
To use external modules inside a decorated method, you must use require() inside the method body with the absolute path to the module.
`typescript
import { RunInNewThread } from 'node-thread-decorator';
import path from 'path';
class MyService {
@RunInNewThread()
async processWithExternal(servicePath: string, data: number[]): Promise
// ✅ Correct - require() inside the method with absolute path
const { Calculator } = require(servicePath);
const calc = new Calculator();
return calc.sum(data);
}
}
// Usage - pass the absolute path as argument
const servicePath = path.resolve(__dirname, './services/calculator');
const result = await service.processWithExternal(servicePath, [1, 2, 3]);
`
Built-in modules can be required directly by name:
`typescript
class MyService {
@RunInNewThread()
async readFileInThread(filePath: string): Promise
const fs = require('fs');
const path = require('path');
return fs.readFileSync(filePath, 'utf-8');
}
@RunInNewThread()
async getSystemInfo(): Promise
`typescript
// services/calculator.ts
export class Calculator {
sum(numbers: number[]): number {
return numbers.reduce((a, b) => a + b, 0);
}
multiply(numbers: number[]): number {
return numbers.reduce((a, b) => a * b, 1);
}
}
// my-service.ts
import { RunInNewThread } from 'node-thread-decorator';
import path from 'path';
class MyService {
private readonly calculatorPath = path.resolve(__dirname, './services/calculator');
@RunInNewThread()
async calculate(calculatorPath: string, numbers: number[]): Promise
const { Calculator } = require(calculatorPath);
const calc = new Calculator();
return calc.sum(numbers);
}
async run(): Promise
// Pass the absolute path as an argument
return this.calculate(this.calculatorPath, [1, 2, 3, 4, 5]);
}
}
`
- Use for CPU-intensive operations (cryptography, data processing, complex calculations)
- Pass all required data as arguments
- Use require() inside the method for external dependencies
- Always use absolute paths when requiring local modules
- Handle errors with try/catch blocks
- Set appropriate timeouts for long-running operations
- Don't use for I/O-bound operations (the event loop handles these efficiently)
- Don't try to access external scope variables
- Don't use this to access instance propertiesrequire()` inside workers
- Don't pass non-serializable data as arguments
- Don't use relative paths with
| Use Case | Recommendation |
|----------|----------------|
| Heavy mathematical computations | ✅ Use decorator |
| Image/video processing | ✅ Use decorator |
| Data encryption/hashing | ✅ Use decorator |
| Database queries | ❌ Use async/await |
| HTTP requests | ❌ Use async/await |
| File I/O | ❌ Use async/await |
MIT License - see LICENSE for details