Performance: Worker pool pattern
This pattern affects how efficiently CPU-intensive tasks are handled without blocking the main event loop, improving responsiveness and throughput.
Jump into concepts and practice - no test required
const http = require('http'); const { Worker } = require('worker_threads'); function runHeavyTask() { return new Promise((resolve, reject) => { const worker = new Worker('./heavyTask.js'); worker.on('message', resolve); worker.on('error', reject); }); } http.createServer(async (req, res) => { const result = await runHeavyTask(); res.end(`Result: ${result}`); }).listen(3000);
const http = require('http'); http.createServer((req, res) => { // Heavy computation directly on main thread let result = 0; for (let i = 0; i < 1e9; i++) { result += i; } res.end(`Result: ${result}`); }).listen(3000);
| Pattern | CPU Usage | Memory Usage | Main Thread Blocking | Verdict |
|---|---|---|---|---|
| Heavy tasks on main thread | High CPU on main thread | Low memory | Blocks main thread causing high INP | [X] Bad |
| Unlimited workers for each task | High CPU across many threads | High memory usage | Less main thread blocking but high overhead | [!] OK |
| Worker pool with limited threads | Balanced CPU across workers | Controlled memory usage | No main thread blocking, smooth responsiveness | [OK] Good |
worker pool pattern in Node.js?worker_threads module?const { Worker } = require('worker_threads');new WorkerPool(4); to create 4 workers.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);
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);
}
}
return new Promise((resolve) => { ... }) so resolve is defined and caller can await result.