npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

node-thread-decorator

v1.0.5

Published

A TypeScript decorator that executes class methods in separate Node.js worker threads

Readme

Node Thread Decorator

node version npm version License: MIT

A TypeScript decorator that executes class methods in separate Node.js worker threads, preventing CPU-intensive operations from blocking the main event loop.

Table of Contents

Installation

npm install node-thread-decorator

Quick Start

import { RunInNewThread } from 'node-thread-decorator';

class Calculator {
  @RunInNewThread()
  async heavyComputation(iterations: number): Promise<number> {
    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);

API Reference

@RunInNewThread(timeout?: number)

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<T> - The decorated method always returns a Promise, even if the original method was synchronous.

Usage Examples

Basic Usage

import { RunInNewThread } from 'node-thread-decorator';

class MyService {
  @RunInNewThread()
  async processData(data: number[]): Promise<number> {
    return data.reduce((sum, n) => sum + n, 0);
  }
}

With Timeout

class MyService {
  @RunInNewThread(5000) // 5 second timeout
  async longRunningTask(): Promise<string> {
    // If this takes more than 5 seconds, it will throw an error
    return 'completed';
  }
}

NestJS Integration

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<string> {
    await this.heavyOperation(10000); // Won't block the main thread
    return 'OK';
  }
}

Important Limitations

1. Class Methods Only

The decorator only works with class methods. Standalone functions are not supported.

// ✅ Supported - Class method
class MyClass {
  @RunInNewThread()
  async myMethod() { /* ... */ }
}

// ❌ Not Supported - Standalone function
@RunInNewThread() // This won't work
function myFunction() { /* ... */ }

2. No Access to External Scope (Closures)

Methods executed in worker threads cannot access variables, imports, or closures from the original scope. Each worker runs in an isolated context.

import { someHelper } from './helpers';

const CONFIG = { maxRetries: 3 };

class MyService {
  @RunInNewThread()
  async process(): Promise<void> {
    // ❌ This will fail - 'someHelper' is not defined in worker
    someHelper();
    
    // ❌ This will fail - 'CONFIG' is not defined in worker
    console.log(CONFIG.maxRetries);
  }
}

3. No Access to this Context

The this keyword inside decorated methods does not refer to the class instance. Instance properties and other methods are not accessible.

class MyService {
  private value = 42;

  @RunInNewThread()
  async getValue(): Promise<number> {
    // ❌ This will fail - 'this.value' is undefined in worker
    return this.value;
  }

  @RunInNewThread()
  async callOther(): Promise<void> {
    // ❌ This will fail - 'this.otherMethod' is not a function in worker
    this.otherMethod();
  }

  otherMethod() { /* ... */ }
}

4. Arguments Must Be Serializable

All arguments passed to decorated methods must be serializable (transferable via postMessage). Functions, class instances, and circular references cannot be passed.

class MyService {
  @RunInNewThread()
  async process(data: unknown): Promise<void> {
    // ...
  }
}

// ✅ 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

Using External Dependencies

To use external modules inside a decorated method, you must use require() inside the method body with the absolute path to the module.

Pattern for External Dependencies

import { RunInNewThread } from 'node-thread-decorator';
import path from 'path';

class MyService {
  @RunInNewThread()
  async processWithExternal(servicePath: string, data: number[]): Promise<number> {
    // ✅ 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]);

Using Node.js Built-in Modules

Built-in modules can be required directly by name:

class MyService {
  @RunInNewThread()
  async readFileInThread(filePath: string): Promise<string> {
    const fs = require('fs');
    const path = require('path');
    return fs.readFileSync(filePath, 'utf-8');
  }

  @RunInNewThread()
  async getSystemInfo(): Promise<object> {
    const os = require('os');
    return {
      cpus: os.cpus().length,
      memory: os.totalmem(),
      platform: os.platform()
    };
  }
}

Complete Example with External Service

// 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<number> {
    const { Calculator } = require(calculatorPath);
    const calc = new Calculator();
    return calc.sum(numbers);
  }

  async run(): Promise<number> {
    // Pass the absolute path as an argument
    return this.calculate(this.calculatorPath, [1, 2, 3, 4, 5]);
  }
}

Best Practices

✅ Do

  • 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

  • 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 properties
  • Don't pass non-serializable data as arguments
  • Don't use relative paths with require() inside workers

When to Use

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

Contributors

License

MIT License - see LICENSE for details