High Performance with Node.js Multithreading and Worker Threads
What Will You Learn in This Guide?
This guide explains why CPU-intensive operations cause problems in Node.js.
Shows you how to improve performance using Worker Threads and worker pools.
Technical Summary
- Subject: Node.js Worker Threads
- Problem: CPU operations block the event loop
- Solution: worker_threads + Piscina
- Goal: Responsive and scalable API
Why Does Node.js Need Multithreading?
Node.js runs JavaScript code on a single thread.
I/O operations do not block thanks to libuv.
However, hashing, encryption, and image processing lock up the CPU.
In this case, the server cannot respond to new requests.
Problem: Example Blocking Main Thread
import express from 'express';
import { createHash } from 'node:crypto';
const app = express();
app.get('/hizli', (req, res) => {
res.send('Bu istek hızlıdır.');
});
app.get('/agir-islem', (req, res) => {
let hash = '0';
for (let i = 0; i < 5_000_000; i++) {
hash = createHash('sha256').update(hash).digest('hex');
}
res.send('Ağır işlem tamamlandı.');
});
app.listen(3000);
- While this code is running, the /agir-process call crashes the entire server.
Solution: Using Worker Threads
- We move the heavy processing to a separate worker file.
// worker.js
import { parentPort, workerData } from 'node:worker_threads';
import { createHash } from 'node:crypto';
let hash = workerData || '0';
for (let i = 0; i < 5_000_000; i++) {
hash = createHash('sha256').update(hash).digest('hex');
}
parentPort.postMessage(hash);
- This file only does CPU work.
Running Worker from Main Thread
import { Worker } from 'node:worker_threads';
function workerCalistir(veri) {
return new Promise((resolve, reject) => {
const worker = new Worker('./worker.js', {
workerData: veri
});
worker.on('message', resolve);
worker.on('error', reject);
});
}
- This function does not block the main thread.
Non-Blocking Endpoint
app.get('/non-blocking', async (req, res) => {
const sonuc = await workerCalistir('baslangic');
res.send(`Worker sonucu: ${sonuc}`);
});
- The server remains responsive while this route is running.
Why Use Worker Pool?
-
Creating workers for each request is expensive.
-
Worker pool reuses threads.
-
This approach reduces memory and latency.
Worker Pool with Piscina
import Piscina from 'piscina';
const pool = new Piscina({
filename: './worker.js',
minThreads: 2,
maxThreads: 4
});
export function havuzlaIslem(veri) {
return pool.run(veri);
}
- Piscina worker automatically manages its life cycle.
When Not to Use Worker Threads?
-
Database queries
-
HTTP requests
-
File reading operations
-These operations are more efficient with async I/O.
Critical Tips for Production
-
Set memory limit per worker
-
Monitor queue occupancy
-
Measure event loop latency
-
Match Docker CPU limits with number of workers
Frequently Asked Questions
1. Is Node.js really multithreaded? Yes. JavaScript runs in parallel with Worker Threads.
2. Is worker used for DB queries? No. These are I/O operations.
3. How many workers should I open? Usually as many CPU cores.
4. Worker or child process? Worker is lighter and faster.
Result
Worker Threads make Node.js suitable for CPU-intensive tasks. With proper pooling, APIs remain fluid even under high load.
You can safely use this architecture on the GenixNode infrastructure.

