Thread or Queue? The Ultimate Guide to Background Processing in Node.js
Node.js runs on a single-threaded event loop. It’s great for I/O tasks (like reading files, making API calls), but struggles with CPU-heavy work because:
- CPU-bound code can block the event loop.
- This means other user requests may hang or timeout while the CPU is busy crunching numbers.
This is where Worker Threads and Queues come in — but they solve different problems.
1. Worker Threads — for CPU-Intensive Tasks
What are Worker Threads?
Worker threads are actual OS threads inside your Node.js process. You can use them to run heavy JavaScript code in parallel, off the main thread.
What They’re Good At:
Benefits
- Avoid event loop blocking for long computations.
- Parallelize work within one process.
- Share memory via
SharedArrayBufferif needed.
Limitations
- No built-in retry/delay logic.
- Managing threads manually can be error-prone.
- Not good for I/O tasks.
Example:
You want to hash 10,000 passwords in a Node API.
Without Worker Thread:
- Every request to hash a password blocks the main thread.
- If 10 requests hit at once, users will face slow or failed responses.
With Worker Thread:
- Each hash runs in a thread → no block, app stays responsive.
2. Queues — for Background Jobs / I/O / Workflows
What is a Job Queue?
A queue is a system to:
- Defer a job to be done later
- Run jobs asynchronously
- Retry on failure
- Throttle / rate limit execution
- Persist jobs in Redis/DB
What They’re Good At:
Benefits
- Job persistence with Redis (survive crash)
- Retry logic built-in
- Delay jobs
- Rate limiting / concurrency control
- Dashboards (like BullMQ UI)
Limitations
- Queues are not for CPU-intensive tasks
- You often need to scale with worker processes (or threads)
Example:
You want to send a welcome email after user registration.
Without Queue:
POST /register→ Email sent immediately → if mail server is slow/down, user waits or errors out.
With Queue:
POST /register→ AddsSendWelcomeEmailjob to the queue.- Main API responds immediately.
- Worker picks up job and sends the email.
- If mail fails, it retries in 30s.
Combine Both: Real-World Hybrid Examples
Here’s where it gets powerful: use both queues and threads together.
i) Slot Game Engine
Why this works well:
- The game logic is CPU-heavy (random reels, payouts, win lines).
- The request is async and user doesn’t need it instantly.
- Using a queue ensures job isn’t lost even if the server crashes.
ii) Video Upload Platform
Why this works well:
- Transcoding is CPU-bound (Worker Thread or child_process).
- User doesn’t wait for the job to finish.
- Queue retries if FFmpeg crashes.
WHEN TO USE WHAT — DECISION TABLE
Suggested Architecture Folder Structure (for hybrid setup)
/src
├── api
│ └── userController.js # Calls queue for background task
├── jobs
│ └── spinQueue.js # BullMQ or BeeQueue
├── workers
│ └── spinWorker.js # Listens to queue and uses thread
├── threads
│ └── spinProcessor.js # Actual logic for slot spin in thread
├── utils
│ └── threadHelper.js # Helper to run tasks in threads