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

mthread-js

v1.0.2

Published

True multithreading for Node.js — auto and manual thread control, built on native worker_threads.

Downloads

165

Readme

mthread-js

True multithreading for Node.js — auto and manual thread control, built on native worker_threads.

npm version Node.js License


JavaScript is single-threaded — but Node.js isn't.
mthread-js gives you a clean, promise-based API to run real parallel OS threads with zero native dependencies.


Features

  • 🚀 Real OS threads — powered by Node.js worker_threads, not fake async
  • 🤖 Auto mode — every task gets its own dedicated thread automatically
  • 🎯 Manual mode — name your threads and decide exactly what runs where
  • ♻️ Thread Pool — classic reusable worker pool for steady workloads
  • 🔗 One-shot tasks — fire a task on a background thread and get a Promise back
  • 🛡️ Safe by default — concurrency cap prevents thread exhaustion
  • 0️⃣ Zero dependencies

Requirements

  • Node.js ≥ 18 (uses worker_threads + node:test)

Installation

npm install mthread-js

Quick Start

1. Write a worker file

// my-worker.js
import { defineWorker } from 'mthread-js';

defineWorker((a, b) => {
  // This runs on a real background thread
  return a + b;
});

2. Use it from your main file

import { runTask } from 'mthread-js';

const result = await runTask('./my-worker.js', [10, 20]);
console.log(result); // 30 — computed on a separate OS thread

API

defineWorker(fn) — Worker side

Call this inside your worker file to register the function that runs on the background thread.

import { defineWorker } from 'mthread-js';

defineWorker(async (x, y) => {
  return x * y;
});

The function can be async. Arguments are passed from the main thread via postMessage (structured-clone serialization).


runTask(workerPath, args) — One-shot thread

Spawns a fresh thread, runs one task, terminates the thread, returns the result.
Best for: infrequent heavy computations.

import { runTask } from 'mthread-js';

const result = await runTask('./my-worker.js', [10, 20]);

| Param | Type | Description | |---|---|---| | workerPath | string | Absolute or relative path to the worker script | | args | Array | Arguments passed to the worker function |


ThreadPool — Reusable thread pool

Creates a fixed pool of persistent threads. Tasks are queued and dispatched to the next idle thread.
Best for: high-frequency tasks where you want bounded concurrency.

import { ThreadPool } from 'mthread-js';

const pool = new ThreadPool('./my-worker.js', 4); // 4 persistent threads

const results = await Promise.all([
  pool.execute([1, 2]),
  pool.execute([3, 4]),
  pool.execute([5, 6]),
]);

await pool.terminate();

Constructor

new ThreadPool(workerScriptPath, size = 4)

| Param | Type | Default | Description | |---|---|---|---| | workerScriptPath | string | — | Path to the worker script | | size | number | 4 | Number of persistent worker threads |

Methods

| Method | Returns | Description | |---|---|---| | execute(args) | Promise<any> | Run a task on the next idle thread | | terminate() | Promise<void> | Shut down all threads |


AutoThreader — Auto threading mode ✨

Every task gets its own dedicated OS thread, spawned automatically. No configuration needed.
Best for: burst workloads where you want maximum parallelism.

import { AutoThreader } from 'mthread-js';

const auto = new AutoThreader('./my-worker.js');

// Single task on its own thread
const result = await auto.run([10, 20]);

// All tasks start simultaneously — each on a separate thread
const results = await auto.runAll([
  [1, 2],
  [3, 4],
  [5, 6],
]);

auto.destroy();

Constructor

new AutoThreader(workerScriptPath, options?)

| Option | Type | Default | Description | |---|---|---|---| | maxConcurrent | number | os.cpus().length × 2 | Max threads running at once. Excess tasks are queued. |

Methods

| Method | Returns | Description | |---|---|---| | run(args) | Promise<any> | Spawn a fresh thread, run task, return result | | runAll(argsArray) | Promise<any[]> | Run all tasks in parallel, one thread each | | destroy() | void | Terminate any lingering threads |


ManualThreader — Manual threading mode 🎯

You name your threads and choose exactly which thread runs which task.
Best for: long-lived specialized workers where routing matters.

import { ManualThreader } from 'mthread-js';

const mt = new ManualThreader('./my-worker.js');

// Create named persistent threads
mt.createThread('alpha');
mt.createThread('beta');
mt.createThread('gamma', './other-worker.js'); // different script per thread

// Route a task to a specific thread
const r1 = await mt.runOn('alpha', [1, 2]);

// Fan-out: same task on several threads in parallel
const results = await mt.runOnMany(['alpha', 'beta'], [10]);
// → [result_from_alpha, result_from_beta]

// Broadcast: hit every thread at once
const all = await mt.broadcast([99]);
// → { alpha: result, beta: result, gamma: result }

// Inspect and manage threads
console.log(mt.listThreads()); // ['alpha', 'beta', 'gamma']
mt.removeThread('gamma');
mt.destroy();

Constructor

new ManualThreader(defaultScriptPath?)

Methods

| Method | Returns | Description | |---|---|---| | createThread(name, scriptPath?) | this | Create a persistent named thread | | runOn(name, args) | Promise<any> | Run a task on a specific named thread | | runOnMany(names[], args) | Promise<any[]> | Run same task on several threads in parallel | | broadcast(args) | Promise<{ [name]: any }> | Run same task on ALL threads, returns named results | | listThreads() | string[] | List all thread names | | removeThread(name) | this | Terminate and remove a named thread | | destroy() | void | Terminate all threads |


Choosing the Right API

| Scenario | Use | |---|---| | One heavy task, fire and forget | runTask() | | Many similar tasks, bounded threads | ThreadPool | | Burst of tasks, max parallelism | AutoThreader.runAll() | | Long-lived workers, route by name | ManualThreader | | Same work across all workers | ManualThreader.broadcast() |


Important: Data Serialization

Worker threads cannot share memory by default. Data passed between the main thread and workers is copied using the structured clone algorithm.

  • ✅ Works: numbers, strings, arrays, plain objects, ArrayBuffer, TypedArray
  • ❌ Doesn't work: functions, class instances, closures, DOM nodes

If you need true shared memory, use SharedArrayBuffer with Atomics.


Error Handling

Every API returns a Promise, and every failure mode below rejects that promise — nothing throws synchronously (the only synchronous throws are constructor misuse and createThread/removeThread misuse, e.g. duplicate or unknown thread names).

Failure modes

| Failure | What you get | |---|---| | Worker function throws / async function rejects | Rejected promise; original message and stack are preserved | | Worker file missing or has a syntax error | Rejected promise with an actionable message (Cannot find module …, SyntaxError: …) | | Worker exits unexpectedly (process.exit(), killed externally) | Rejected with code: 'ERR_WORKER_UNEXPECTED_EXIT' | | Receive-side structured-clone failure (messageerror) | Rejected with code: 'ERR_WORKER_MESSAGEERROR' (original error in .cause) | | Args/result not structured-cloneable (e.g. functions) | Rejected with the DataCloneError (… could not be cloned.) | | terminate()/destroy()/removeThread() called while tasks are pending | Pending + queued tasks reject with code: 'ERR_TERMINATED' | | Calling execute()/run()/runOn() after termination | Rejects with code: 'ERR_TERMINATED' | | Every worker in a pool exits; queued tasks can never start | Rejected with code: 'ERR_NO_WORKERS' |

No built-in timeout

If a worker never responds (e.g. an infinite loop), the task's promise stays pending — there is no default timeout. This is a deliberate trade-off: a timeout cannot distinguish "still computing" from "dead", and killing a busy worker mid-computation can leave your program in an undefined state. Use terminate()/destroy() (they reject pending tasks) or race your own timer:

const result = await Promise.race([
  pool.execute([10, 20]),
  new Promise((_, reject) =>
    setTimeout(() => reject(new Error('task timed out')), 5000)
  ),
]);

Example: catching failures

import { ThreadPool } from 'mthread-js';

const pool = new ThreadPool('./my-worker.js', 4);

try {
  const result = await pool.execute([10, 20]);
} catch (err) {
  if (err.code === 'ERR_WORKER_MESSAGEERROR') {
    // Structured-clone failure — the message never arrived intact
  } else if (err.code === 'ERR_WORKER_UNEXPECTED_EXIT') {
    // The worker crashed while this task was in flight
  } else if (err.code === 'ERR_TERMINATED') {
    // The pool was terminated before the task finished
  } else {
    // Normal worker error — err.message / err.stack come from the worker
  }
} finally {
  await pool.terminate();
}

Multi-task semantics: fail-fast

AutoThreader.runAll(), ManualThreader.runOnMany() and ManualThreader.broadcast() use Promise.all semantics: the returned promise rejects as soon as the first task fails. Sibling tasks keep running to completion on their threads, but their results are discarded. If you need per-task outcomes instead, submit tasks individually and use Promise.allSettled() yourself.

runTask() specifics

runTask() spawns a pool of one, runs your task, then terminates it — the rejection behavior is identical to ThreadPool. Because the pool is internal, a never-responding worker leaves the runTask promise pending forever; prefer ThreadPool/ManualThreader when you need the terminate escape hatch.


Examples

See the examples/ folder for runnable demos.

node examples/demo.js

License

ISC © 2026 AtaberkCelil