Node.js Background Work: Polling or Events?
- Polling overhead vs event
- %
- Result
- Check interval
- 5 seconds
- You entered
- Work duration
- 30 seconds
- You entered
For background work in Node.js, events are almost always the better pattern than polling. Events deliver work the moment it happens, use fewer resources, and scale naturally; polling checks on a fixed schedule, wasting CPU and adding latency. This article explains both patterns, when polling might still make sense, and how to choose between them for your specific use case.
Formula
Checks per work cycle = Work duration / Check interval
Overhead percentage = ((Work duration / Check interval) – 1) × 100
Where:
- Work duration (seconds) – how long the background task actually runs.
- Check interval (seconds) – how often polling checks for new work.
- Checks per work cycle – number of polls during one task; each poll is a wasted call if no work is ready.
- Overhead percentage – extra checks compared to a single event notification.
Worked Example
With defaults: work duration = 30 s, check interval = 5 s.
Checks per work cycle = 30 / 5 = 6 checks.
Overhead = ((30 / 5) – 1) × 100 = 500%.
Polling makes 6 checks for one piece of work; an event would notify once. The result matches the calculator: 6 checks, 500% overhead.
What “Background Work� Means in Node.js
Node.js runs on a single thread for JavaScript, but that thread can delegate heavy tasks to the operating system or worker threads. Background work includes:
- Reading/writing files
- Network requests (HTTP, database calls)
- CPU-heavy computations (image processing, data transformation)
- Timers (setTimeout, setInterval)
- Message queue consumers
All of these need a way to know when work is available or complete. That’s where polling and events come in.
How Polling Works
Polling repeatedly checks a condition at a fixed interval. In Node.js, you might use setInterval or a while loop with a sleep. Example:
setInterval(() => {
const job = checkQueue(); // query DB or read file
if (job) processJob(job);
}, 5000);
This checks every 5 seconds. If a job arrives 1 second after a check, it waits 4 seconds. If no job arrives, the check still runs, wasting CPU and I/O.
How Events Work
Events notify your code when something happens. Node.js has built-in support: EventEmitter, streams, child_process messages, and worker_threads messages. Example:
const { EventEmitter } = require('events');
const emitter = new EventEmitter();
emitter.on('job', (job) => processJob(job));
// elsewhere:
emitter.emit('job', newJob);
For external sources, you use callbacks or promises: fs.watch for file changes, database clients that emit events when results arrive, or HTTP servers that emit on requests. Events are push-based: the source tells your code when work is ready.
How to Choose: Polling vs Events
Start with events. They are the idiomatic Node.js pattern and almost always the right choice for I/O-bound tasks. Polling only makes sense when:
- The work source has no event mechanism (e.g., a legacy system that only exposes a status file).
- You need to detect a condition that has no push API (e.g., checking if a network drive is mounted).
- The check is extremely cheap and the interval is long (e.g., once a day).
- You are dealing with a third-party service that rate-limits event subscriptions.
For everything else, use events. If you must poll, choose a reasonable interval: too short wastes resources, too long adds latency. Consider exponential backoff – start with 1 second, then double up to 60 seconds if no work is found.
Common Problems and Misconceptions
“Events are always faster.� Not necessarily – the event system has overhead, but it’s negligible compared to polling’s wasted checks. For a job that runs 30 seconds, polling every 5 seconds adds 500% more checks.
“Polling is simpler to implement.� Sometimes true, but events are not much harder. The EventEmitter API is tiny. For external sources, most Node.js libraries already use events (streams, HTTP, database drivers).
“Polling is more reliable because you always check.� Events can be missed if you attach the listener after the event fires. That’s a real risk, but you can handle it by checking once at startup and then listening. Polling never misses work, but it can delay it.
“setInterval is bad.� It’s fine for the right use, but it keeps the event loop busy even when idle. Use setTimeout recursively if you want to avoid overlapping checks.
Comparisons: Polling vs Events in Practice
| Aspect | Polling | Events |
|---|---|---|
| Latency | Up to interval delay | Immediate |
| CPU usage | Wasted when idle | Near zero when idle |
| I/O usage | Frequent queries | Only on change |
| Complexity | Simple but manual | Requires event source |
| Scalability | Poor for many tasks | Good |
Real-world example: a file watcher. Polling would stat the file every second; events use fs.watch which gets notified by the OS. The latter is far more efficient.
Alternatives: Queues, Streams, and Worker Threads
Sometimes the choice isn’t just polling vs events, but which event-based tool to use:
- Message queues (e.g., RabbitMQ, Redis) – they push messages to consumers; perfect for background jobs.
- Streams – handle data in chunks as it arrives; good for large files or network data.
- Worker threads – run CPU-heavy tasks in parallel; they communicate via messages (events).
- Child processes – similar to workers, but separate processes; use
process.on('message').
These are all event-driven. If you’re building a background job system, consider a queue library instead of raw polling.
Frequently Asked Questions
Is polling ever acceptable in Node.js?
Yes, for rare, cheap checks. For example, checking a configuration file every 5 minutes is fine. The problem is when you poll frequently for work that arrives sporadically.
How do I choose the polling interval?
Balance latency vs. resource usage. If work must be processed within 1 second, poll every 0.5 seconds. If it can wait 1 minute, poll every 30 seconds. Use exponential backoff when idle.
Can I combine polling and events?
Yes. For example, poll every 30 seconds to check for a new job, but also listen for an event that signals immediate work. This gives you a safety net.
What is the “event loop� and how does it relate?
The event loop is Node.js’s core mechanism that handles callbacks. Events are processed through it. Polling uses timers, which also go through the event loop, but they fire even when no work is ready, wasting cycles.
Does using events guarantee no missed work?
No. If you attach a listener after an event fires, you miss it. Always attach listeners early, or check state once before listening.
What about using async/await with events?
You can wrap events in promises, but be careful with one-time events. For repeated events, use EventEmitter or for await...of with async iterators (e.g., readline).
Related
Formula used
in t | Check interval | seconds | 5 | 1 in d | Work duration | seconds | 30 | 1 out checks | Checks per work cycle | checks | d / t | 0 out overhead | Polling overhead vs event | % | ((d / t) - 1) * 100 | 0
Each line is one input (in, sel, list) or one result (out: label, unit, formula, decimals). How the calculators work
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.