Calls fn on a repeating interval, pausing automatically when the browser tab is hidden.
Self-paces via recursive setTimeout rather than a fixed setInterval: the next call is
scheduled after the previous one settles, not from a fixed clock. This keeps at most one
call in flight at a time — if fn takes longer than intervalMs (e.g. a slow subprocess
spawn on constrained hardware), later ticks are delayed rather than stacking concurrent
calls on top of each other.
It also backs off adaptively based on cost, not just deadline misses: if a call ate more
than TARGET_DUTY_CYCLE of its own interval, the next one is delayed enough to bring that
ratio back down (capped at intervalMs * MAX_BACKOFF_MULTIPLIER) — a call that's technically
"on time" but consistently expensive still gets backed off, not just one that overruns
outright. A cheap, fast call keeps the normal intervalMs cadence, so this self-tunes down
on slow hardware and back up again as soon as calls are cheap, with no configuration needed.
Parameters
fn: ()=>void|Promise<void>
The callback to invoke on each tick. May be async — rejections are swallowed.
intervalMs: number
Interval duration in milliseconds.
paused: boolean = false
When true, suspends polling without tearing down the effect.
Calls
fnon a repeating interval, pausing automatically when the browser tab is hidden.Self-paces via recursive
setTimeoutrather than a fixedsetInterval: the next call is scheduled after the previous one settles, not from a fixed clock. This keeps at most one call in flight at a time — iffntakes longer thanintervalMs(e.g. a slow subprocess spawn on constrained hardware), later ticks are delayed rather than stacking concurrent calls on top of each other.It also backs off adaptively based on cost, not just deadline misses: if a call ate more than
TARGET_DUTY_CYCLEof its own interval, the next one is delayed enough to bring that ratio back down (capped atintervalMs * MAX_BACKOFF_MULTIPLIER) — a call that's technically "on time" but consistently expensive still gets backed off, not just one that overruns outright. A cheap, fast call keeps the normalintervalMscadence, so this self-tunes down on slow hardware and back up again as soon as calls are cheap, with no configuration needed.