The worker pool pattern helps run many tasks at the same time without slowing down your main program. It uses a group of workers to share the work.
Worker pool pattern in Node.js
Start learning this pattern below
Jump into concepts and practice - no test required
const { Worker } = require('worker_threads');
class WorkerPool {
constructor(numWorkers) {
this.workers = [];
this.freeWorkers = [];
for (let i = 0; i < numWorkers; i++) {
const worker = new Worker('./worker.js');
this.workers.push(worker);
this.freeWorkers.push(worker);
}
}
runTask(taskData) {
return new Promise((resolve, reject) => {
if (this.freeWorkers.length === 0) {
reject(new Error('No free workers'));
return;
}
const worker = this.freeWorkers.pop();
worker.once('message', (result) => {
this.freeWorkers.push(worker);
resolve(result);
});
worker.once('error', reject);
worker.postMessage(taskData);
});
}
close() {
for (const worker of this.workers) {
worker.terminate();
}
}
}The Worker class comes from the worker_threads module in Node.js.
You create a pool by making several workers and keep track of which are free.
const pool = new WorkerPool(3); pool.runTask({ number: 10 }).then(console.log);
pool.runTask({ number: 20 }).then(result => {
console.log('Result:', result);
});This example shows a worker file that squares a number sent to it. The main file creates a pool of 2 workers and runs 3 tasks. It prints the array of squared results.
// worker.js
const { parentPort } = require('worker_threads');
parentPort.on('message', (task) => {
// Simple task: square the number
const result = task.number * task.number;
parentPort.postMessage(result);
});
// main.js
const { Worker } = require('worker_threads');
class WorkerPool {
constructor(numWorkers) {
this.workers = [];
this.freeWorkers = [];
for (let i = 0; i < numWorkers; i++) {
const worker = new Worker('./worker.js');
this.workers.push(worker);
this.freeWorkers.push(worker);
}
}
runTask(taskData) {
return new Promise((resolve, reject) => {
if (this.freeWorkers.length === 0) {
reject(new Error('No free workers'));
return;
}
const worker = this.freeWorkers.pop();
worker.once('message', (result) => {
this.freeWorkers.push(worker);
resolve(result);
});
worker.once('error', reject);
worker.postMessage(taskData);
});
}
close() {
for (const worker of this.workers) {
worker.terminate();
}
}
}
(async () => {
const pool = new WorkerPool(2);
const results = await Promise.all([
pool.runTask({ number: 5 }),
pool.runTask({ number: 10 }),
pool.runTask({ number: 3 })
]).catch(console.error);
console.log(results);
pool.close();
})();Workers run in separate threads, so they do not block the main program.
Always close workers when done to free resources.
If no workers are free, tasks can be queued or rejected depending on your design.
The worker pool pattern helps run many tasks at the same time efficiently.
It uses a fixed number of workers to share the work and keep your program fast.
You create workers, send tasks, get results, and then reuse workers for new tasks.
Practice
worker pool pattern in Node.js?Solution
Step 1: Understand the worker pool pattern concept
The worker pool pattern uses a limited number of workers to handle many tasks efficiently without creating too many threads.Step 2: Identify the main goal in Node.js context
It allows running tasks concurrently but limits the number of workers to keep resource use balanced and improve speed.Final Answer:
To run multiple tasks concurrently using a fixed number of workers -> Option DQuick Check:
Worker pool = fixed workers + concurrent tasks [OK]
- Thinking worker pool creates unlimited workers
- Believing it slows down the program
- Confusing it with single-threaded execution
worker_threads module?Solution
Step 1: Import Worker correctly from worker_threads
The correct import is:const { Worker } = require('worker_threads');Step 2: Create a worker pool instance properly
Assuming a WorkerPool class exists, it should be instantiated withnew WorkerPool(4);to create 4 workers.Final Answer:
const { Worker } = require('worker_threads'); const pool = new WorkerPool(4); -> Option AQuick Check:
Correct import + new instance = const { Worker } = require('worker_threads'); const pool = new WorkerPool(4); [OK]
- Calling WorkerPool without new keyword
- Wrong order of import and instantiation
- Using Worker constructor incorrectly
const { Worker } = require('worker_threads');
class WorkerPool {
constructor(size) {
this.workers = Array(size).fill(null).map(() => new Worker('./worker.js'));
}
runTask(task) {
return new Promise((resolve) => {
const worker = this.workers.pop();
worker.once('message', (result) => {
this.workers.push(worker);
resolve(result);
});
worker.postMessage(task);
});
}
}
const pool = new WorkerPool(2);
pool.runTask('task1').then(console.log);
pool.runTask('task2').then(console.log);
Solution
Step 1: Understand worker allocation and reuse
The pool starts with 2 workers. Each runTask pops a worker, uses it, then pushes it back after message received.Step 2: Analyze concurrency and output order
Both tasks run concurrently on separate workers. Results log as tasks complete, order may vary.Final Answer:
Outputs results of task1 and task2 as they complete, order not guaranteed -> Option BQuick Check:
Workers reused, tasks run concurrently [OK]
- Assuming workers array empties causing error
- Thinking tasks run one after another
- Believing only first task logs
class WorkerPool {
constructor(size) {
this.workers = [];
for (let i = 0; i < size; i++) {
this.workers.push(new Worker('./worker.js'));
}
}
runTask(task) {
if (this.workers.length === 0) {
throw new Error('No workers available');
}
const worker = this.workers.pop();
worker.once('message', (result) => {
resolve(result);
this.workers.push(worker);
});
worker.postMessage(task);
}
}
Solution
Step 1: Check runTask return and resolve usage
runTask uses resolve inside callback but does not return a Promise or define resolve, causing error.Step 2: Fix by wrapping runTask in a Promise and returning it
Wrap the logic insidereturn new Promise((resolve) => { ... })so resolve is defined and caller can await result.Final Answer:
Add a Promise wrapper around runTask and return it -> Option CQuick Check:
runTask must return Promise with resolve [OK]
- Ignoring missing Promise causes runtime error
- Removing worker availability check breaks logic
- Terminating worker too early stops reuse
Solution
Step 1: Understand resource limits and worker pool size
Creating too many workers (10) can overload CPU and memory. Using 3 workers limits resource use.Step 2: Use a task queue to assign tasks as workers become free
Queue tasks and assign next task to a worker when it finishes ensures all tasks run efficiently without overload.Final Answer:
Create 3 workers, queue tasks, assign next task when a worker finishes -> Option AQuick Check:
Fixed workers + queued tasks = efficient processing [OK]
- Creating too many workers causing overload
- Running tasks sequentially wasting concurrency
- Assigning all tasks to one worker blocking others
