π§΅ Thready
A lightweight, type-safe thread pool implementation for JavaScript and TypeScript that enables true multithreading using Web Workers.
Note: Thready is a worker pool manager only. You provide your own worker script with your custom logic.
Features
- β¨ Simple API - Easy to use singleton pattern for quick integration
- π Worker Pooling - Efficiently reuses worker threads instead of creating new ones
- π¦ Zero Dependencies - No external dependencies, just pure JavaScript
- πͺ TypeScript Support - Full type definitions included
- β‘ Performance - Supports transferable objects for zero-copy data transfer
- π― Smart Queue Management - Automatically manages task queuing and load balancing
- π Statistics - Built-in monitoring of pool state and peβ¦
π§΅ Thready
A lightweight, type-safe thread pool implementation for JavaScript and TypeScript that enables true multithreading using Web Workers.
Note: Thready is a worker pool manager only. You provide your own worker script with your custom logic.
Features
- β¨ Simple API - Easy to use singleton pattern for quick integration
- π Worker Pooling - Efficiently reuses worker threads instead of creating new ones
- π¦ Zero Dependencies - No external dependencies, just pure JavaScript
- πͺ TypeScript Support - Full type definitions included
- β‘ Performance - Supports transferable objects for zero-copy data transfer
- π― Smart Queue Management - Automatically manages task queuing and load balancing
- π Statistics - Built-in monitoring of pool state and performance
- π¨ Bring Your Own Worker - You write the worker logic, we handle the pooling
Installation
npm install thready-js
or with yarn:
yarn add thready-js
Quick Start
1. Initialize Thready in Your Project
Run the following command to generate configuration and worker template files:
npx thready init
This creates a thready-js/ folder with:
thready.config.js- Pre-configured thready instancethready.worker.mjs- Node.js worker templatethready.worker.js- Browser worker template
2. Customize Your Worker
Edit thready-js/thready.worker.mjs (or .js for browser) to add your custom task handlers:
// thready-js/thready.worker.mjs
import { parentPort } from 'worker_threads';
if (!parentPort) {
throw new Error('This script must be run as a worker thread');
}
parentPort.on('message', (message) => {
const { id, taskType, payload } = message;
try {
let result;
// Add your custom task handlers here
switch (taskType) {
case 'fibonacci':
result = fibonacci(payload);
break;
case 'processData':
result = processData(payload);
break;
default:
throw new Error(\`Unknown task type: \${taskType}\`);
}
parentPort.postMessage({ id, type: 'result', payload: result });
} catch (error) {
parentPort.postMessage({ id, type: 'error', payload: error.message });
}
});
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
function processData(data) {
// Your heavy computation here
return data.map(x => x * 2);
}
3. Use Thready in Your Application
Simply import the pre-configured instance and start executing tasks:
import thready from './thready-js/thready.config.js';
// Execute tasks - thready is already initialized!
async function calculate() {
try {
const result = await thready.execute('fibonacci', 40);
console.log('Result:', result);
} catch (error) {
console.error('Task failed:', error);
}
}
// Cleanup on shutdown
process.on('SIGINT', () => {
thready.shutdown();
process.exit(0);
});
Thatβs it! No manual initialization needed - just import and use.
Advanced Usage
Customizing Configuration
Edit thready-js/thready.config.js to customize worker count or worker path:
// thready-js/thready.config.js
import thready from 'thready-js';
import { Worker } from 'worker_threads';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Initialize with custom settings
thready.init({
maxWorkers: 8, // Increase worker count
worker: () => new Worker(join(__dirname, './thready.worker.mjs'), { type: 'module' }),
});
export default thready;
Using with Vite or Webpack
For browser environments, update the config to use Web Workers:
// thready-js/thready.config.js
import thready from 'thready-js';
// Vite/Webpack
thready.init({
maxWorkers: 4,
worker: () => new Worker(new URL('./thready.worker.js', import.meta.url), { type: 'module' })
});
export default thready;
Transferable Objects (Zero-Copy)
For better performance with large data, use transferables:
import thready from './thready-js/thready.config.js';
async function processImage(imageData) {
const buffer = imageData.buffer;
// Transfer ownership of the buffer (zero-copy)
const result = await thready.execute(
'processImage',
buffer,
[buffer] // Transferables array
);
return result;
}
Direct API Usage (Advanced)
If you prefer manual control, you can import threadPool directly:
import { threadPool } from 'thready-js';
import { Worker } from 'worker_threads';
// Manual initialization
threadPool.init({
maxWorkers: 4,
worker: () => new Worker('./custom-worker.mjs')
});
const result = await threadPool.execute('taskType', payload);
import { WorkerPool } from 'thready';
### Using WorkerPool Directly
For even more control, use the `WorkerPool` class:
```javascript
import { WorkerPool } from 'thready-js';
import { Worker } from 'worker_threads';
const pool = new WorkerPool({
maxWorkers: 8,
worker: () => new Worker('./worker.mjs')
});
const result = await pool.run('taskType', payload);
// Get statistics
const stats = pool.getStats();
console.log(stats);
// {
// totalWorkers: 8,
// availableWorkers: 6,
// activeTasks: 2,
// queuedTasks: 0
// }
// Cleanup
pool.terminate();
Monitoring Pool Statistics
import thready from './thready-js/thready.config.js';
const stats = thready.getStats();
console.log('Pool status:', stats);
// {
// totalWorkers: 4,
// availableWorkers: 2,
// activeTasks: 2,
// queuedTasks: 5
// }
Framework Examples
React
import thready from './thready-js/thready.config.js';
import { useEffect, useState } from 'react';
function App() {
const [result, setResult] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
// Cleanup on unmount
return () => thready.shutdown();
}, []);
const handleCalculate = async () => {
setLoading(true);
try {
const res = await thready.execute('fibonacci', 40);
setResult(res);
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
};
return (
<div>
<button onClick={handleCalculate} disabled={loading}>
{loading ? 'Calculating...' : 'Calculate Fibonacci'}
</button>
{result && <p>Result: {result}</p>}
</div>
);
}
Node.js with Worker Threads
The generated thready-js/thready.worker.mjs is already set up for Node.js worker_threads. Just add your task handlers and use it:
// index.js
import thready from './thready-js/thready.config.js';
async function main() {
const result = await thready.execute('fibonacci', 35);
console.log('Result:', result);
thready.shutdown();
}
main();
API Reference
CLI Commands
npx thready init- Generate configuration and worker template files inthready-js/folder
thready (Default Export)
The pre-configured singleton instance from thready-js/thready.config.js.
Methods
execute<T>(taskType: string, payload: any, transferables?: Transferable[]): Promise<T>
Executes a task on the thread pool.
taskType: Identifier for the type of workpayload: Data to be processedtransferables(optional): Array of transferable objects- Returns: Promise resolving to the result
getStats(): object | null
Returns current pool statistics.
shutdown(): void
Terminates all workers and releases resources.
threadPool (Named Export - Advanced)
The singleton instance for manual initialization.
Methods
init(config: WorkerPoolConfig): void
Initializes the thread pool with your worker implementation.
config.maxWorkers(optional): Maximum number of workers (defaults to CPU cores)config.worker: Path to worker script or factory function that returns a Worker
execute<T>(taskType: string, payload: any, transferables?: Transferable[]): Promise<T>
getStats(): object | null
shutdown(): void
WorkerPool
The underlying pool implementation for direct usage.
Constructor
new WorkerPool(config: WorkerPoolConfig)
Methods
run<T>(taskType: string, payload: any, transferables?: Transferable[]): Promise<T>getStats(): objectterminate(): void
TypeScript
Full TypeScript support with exported types:
import thready from './thready-js/thready.config.js';
import type {
WorkerPoolConfig,
WorkerMessage,
WorkerResponse,
Task
} from 'thready-js';
// Custom typed execution
interface CalculationResult {
value: number;
duration: number;
}
const result = await thready.execute<CalculationResult>('calculate', { n: 100 });
Best Practices
- Run
npx thready init: Start with generated templates for quick setup - Customize Workers: Edit
thready-js/thready.worker.mjs(or.js) with your task handlers - Import Config: Use
import thready from './thready-js/thready.config.js'- already initialized - Cleanup: Always call
thready.shutdown()when your application closes - Transferables: Use transferables for large data (ArrayBuffers, ImageData) to avoid copying
- Error Handling: Always wrap
execute()calls in try-catch blocks - Task Granularity: Break large tasks into smaller chunks for better load balancing
Performance Tips
- Use transferables for data > 1MB
- Adjust
maxWorkersinthready-js/thready.config.jsto match your workload - Monitor stats with
thready.getStats()to tune pool size - Reuse the same worker script for multiple task types
- Keep worker scripts focused and pure
Browser Support
Works in all modern browsers that support Web Workers:
- Chrome 4+
- Firefox 3.5+
- Safari 4+
- Edge (all versions)
License
ISC Β© Ram Krishna Yadav
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Issues
Found a bug or have a feature request? Open an issue