@nxtedition/timers
v1.0.4
Published
Optimized timer pooling for Node.js. Delays >= 1000ms are batched into a single 500ms-resolution interval, reducing the number of active handles and improving performance when many long-lived timers are in use.
Keywords
Readme
@nxtedition/timers
Optimized timer pooling for Node.js. Delays >= 1000ms are batched into a single 500ms-resolution interval, reducing the number of active handles and improving performance when many long-lived timers are in use.
Short delays (< 1000ms) fall through to the native setTimeout for full resolution.
Install
npm install @nxtedition/timersUsage
import { setTimeout, clearTimeout } from '@nxtedition/timers'
// Short delays use native setTimeout (full resolution)
const handle = setTimeout(
(data) => {
console.log(data)
},
50,
'hello',
)
// Long delays use pooled timers (500ms resolution, lower overhead)
const pooled = setTimeout(
(data) => {
console.log(data)
},
5000,
'world',
)
// Clear works for both
clearTimeout(handle)
clearTimeout(pooled)Opaque data
The third argument is passed directly to the callback, avoiding closure allocations:
setTimeout(
(ctx) => {
ctx.connection.write(ctx.payload)
},
5000,
{ connection, payload },
)Refreshing timers
Pooled timers (delay >= 1000ms) support .refresh() to reset the delay without creating a new timer:
const handle = setTimeout(
() => {
console.log('idle timeout')
},
30000,
undefined,
)
// On activity, reset the timer
handle.refresh()API
setTimeout(callback, delay, opaque)
Schedule callback(opaque) after delay milliseconds.
delay < 1000— delegates to nativeglobalThis.setTimeoutdelay >= 1000— uses the pooled timer mechanism (500ms resolution)
Returns a handle that can be passed to clearTimeout.
clearTimeout(handle)
Cancel a pending timer. Works with both native and pooled handles. Passing null or undefined is a no-op.
How it works
All pooled timers share a single setTimeout handle that fires every 500ms. On each tick, pending timers are checked and expired ones are fired. This means pooled timers have at most ~500ms of jitter but dramatically reduce the number of active OS timer handles when many long-lived timers are in use (e.g. connection idle timeouts, retry delays).
The internal interval is unref()'d so it does not keep the process alive.
Benchmark
yarn benchmarkResults on Apple M3 Pro / Node.js 25.3.0:
| Benchmark | native setTimeout | pooled setTimeout | speedup | | ------------------------------ | ----------------: | ----------------: | ------: | | create + clear | 250.91 ns | 58.90 ns | 4.3x | | create + clear (GC) | 1.95 µs | 73.99 ns | 26.4x | | batch create + clear 100x (GC) | 41.84 µs | 9.80 µs | 4.3x |
Pooled timers avoid native OS timer handles, which are expensive C++ objects for the GC to trace. The advantage grows under GC pressure — up to 26x faster when GC is forced per iteration.
License
MIT
