node-worker-threads-pool
Simple worker threads pool using Node's worker_threads module. Compatible with ES6+ Promise, Async/Await and TypeScript🚀.
With this library, you can:
- Use
StaticPool
to create a threads pool with a task from worker file or from task function provided to make use of multi-core processor. - Use
DynamicPool
to create a threads pool with different tasks provided each call. Thus you can get more flexibility than StaticPool
and make use of multi-core processor. - Gain extra controllability for the underlying threads by the power of worker_threads, like resourceLimits, SHARE_ENV, transferList and more.
Notification
- This module can only run in Node.js.
Installation
npm install node-worker-threads-pool --save
Simple Example
Quickly create a pool with static task:
const { StaticPool } = require('node-worker-threads-pool');
const staticPool = new StaticPool({
size: 4,
task: (n) => n + 1
});
staticPool.exec(1).then((result) => {
console.log('result from thread pool:', result);
});
There you go! 🎉
Create a pool with dynamic task:
const { DynamicPool } = require('node-worker-threads-pool');
const dynamicPool = new DynamicPool(4);
dynamicPool
.exec({
task: (n) => n + 1,
param: 1
})
.then((result) => {
console.log(result);
});
dynamicPool
.exec({
task: (n) => n + 2,
param: 1
})
.then((result) => {
console.log(result);
});
About the differences between StaticPool and DynamicPool, please see this issue.
API
Class: StaticPool
Instance of StaticPool is a threads pool with static task provided.
new StaticPool(opt)
opt
<Object>
size
<number>
Number of workers in this pool.task
<string | function>
Static task to do. It can be a absolute path of worker file (usage here) or a function. ⚠️Notice: If task is a function, you can NOT access variables defined outside the task function! If you do want to use external data, use workerData to pass some cloneable data.workerData
<any>
Cloneable data you want to access in task function. (usage here)shareEnv
<boolean>
Set true
to enable SHARE_ENV for all threads in pool.resourceLimits
<Object>
Set resourceLimits for all threads in pool.
Example with worker file
In the worker.js :
const { parentPort, workerData } = require('worker_threads');
function fib(n) {
if (n < 2) {
return n;
}
return fib(n - 1) + fib(n - 2);
}
parentPort.on('message', (param) => {
if (typeof param !== 'number') {
throw new Error('param must be a number.');
}
const result = fib(param);
console.log('workerData is', workerData);
parentPort.postMessage(result);
});
In the main.js :
const { StaticPool } = require('node-worker-threads-pool');
const filePath = 'absolute/path/to/your/worker/script';
const pool = new StaticPool({
size: 4,
task: filePath,
workerData: 'workerData!'
});
for (let i = 0; i < 20; i++) {
(async () => {
const num = 40 + Math.trunc(10 * Math.random());
const res = await pool.exec(num);
console.log(`Fibonacci(${num}) result:`, res);
})();
}
Access workerData in task function
You can access workerData in task function using this
keyword:
const pool = new StaticPool({
size: 4,
workerData: 'workerData!',
task() {
console.log(this.workerData);
}
});
⚠️Remember not to use arrow function as a task function when you use this.workerData
, because arrow function don't have this
binding.
staticPool.exec(param)
param
<any>
The param your worker script or task function need.- Returns:
<Promise>
The simplest way to execute a task without considering other configurations. This will choose an idle worker in the pool to execute your heavy task with the param you provided. The Promise is resolved with the result.
staticPool.createExecutor()
- Returns:
<StaticTaskExecutor>
Create a task executor of this pool. This is used to apply some advanced settings to a task. See more details of StaticTaskExecutor.
staticPool.destroy()
Call worker.terminate()
for every worker in the pool and release them.
Class: StaticTaskExecutor
Executor for StaticPool. Used to apply some advanced settings to a task.
Example
const staticPool = new StaticPool({
size: 4,
task: (buf) => {
}
});
const buf = Buffer.alloc(1024 * 1024);
staticPool
.createExecutor()
.setTimeout(1000)
.setTransferList([buf.buffer])
.exec(buf)
.then(() => console.log('done!'));
staticTaskExecutor.setTimeout(t)
t
<number>
timeout in millisecond.- Returns:
<StaticTaskExecutor>
Set timeout for this task.
staticTaskExecutor.setTransferList(transferList)
transferList
<Object[]>
- Returns:
<StaticTaskExecutor>
Set transferList for this task. This is useful when you want to pass some huge data into worker thread.
staticTaskExecutor.exec(param)
param
<any>
- Returns:
<Promise>
Execute this task with the parameter and settings provided. The Promise is resolved with the result your task returned.
Class: DynamicPool
Instance of DynamicPool is a threads pool executes different task functions provided every call.
new DynamicPool(size[, opt])
size
<number>
Number of workers in this pool.opt
shareEnv
<boolean>
Set true
to enable SHARE_ENV for every threads in pool.resourceLimits
<Object>
Set resourceLimits for all threads in pool.
dynamicPool.exec(opt)
opt
task
<function>
Function as a task to do. ⚠️Notice: You can NOT access variables defined outside the task function!timeout
<number>
Timeout in milisecond for limiting the execution time. When timeout, the function will throw a TimeoutError
, use isTimeoutError
function to detect it.
- Returns:
<Promise>
Choose one idle worker in the pool to execute your task function. The Promise is resolved with the result your task returned.
dynamicPool.createExecutor(task)
task
Function
task function.- Returns:
<DynamicTaskExecutor>
Create a task executor of this pool. This is used to apply some advanced settings to a task. See more details of DynamicTaskExecutor.
dynamicPool.destroy()
Call worker.terminate()
for every worker in the pool and release them.
Class: DynamicTaskExecutor
Executor for DynamicPool. Used to apply some advanced settings to a task.
Example
const dynamicPool = new DynamicPool(4);
const buf = Buffer.alloc(1024 * 1024);
dynamicPool
.createExecutor((buf) => {
})
.setTimeout(1000)
.setTransferList([buf.buffer])
.exec(buf)
.then(() => console.log('done!'));
dynamicTaskExecutor.setTimeout(t)
t
<number>
timeout in millisecond.- Returns:
<DynamicTaskExecutor>
Set timeout for this task.
dynamicTaskExecutor.setTransferList(transferList)
transferList
<Object[]>
- Returns:
<DynamicTaskExecutor>
Set transferList for this task. This is useful when you want to pass some huge data into worker thread.
dynamicTaskExecutor.exec(param)
param
<any>
- Returns:
<Promise>
Execute this task with the parameter and settings provided. The Promise is resolved with the result your task returned.
function: isTimeoutError
Detect if a thrown error is TimeoutError
.
isTimeoutError(err)
err <Error>
The error you want to detect.- Returns
<boolean>
true
if the error is a TimeoutError
.
Example
const { isTimeoutError } = require("node-worker-threads-pool");
...
const timeout = 1000;
try {
const res = await staticPool.exec(param, timeout);
} catch (err) {
if (isTimeoutError(err)) {
} else {
}
}
const timeout = 1000;
try {
const res = await dynamicPool.exec({
task() {
},
timeout
});
} catch (err) {
if (isTimeoutError(err)) {
} else {
}
}
Integration with Webpack
If you are using webpack in your project and want to import third-party libraries in task function, please use this.require
:
const staticPool = new StaticPool({
size: 4,
task() {
const lib = this.require('lib');
}
});
const dynamicPool = new DynamicPool(4);
dynamicPool
.exec({
task() {
const lib = this.require('lib');
}
})
.then((result) => {
});