Build a cancellable repeating timer. Why can setInterval drift, and how do you build a self-correcting one with setTimeout?
Timer lifecycle (returning a cancel handle), and understanding interval drift vs a recursive setTimeout that corrects for it.
Two things are being probed. First, ergonomics: wrap the timer so it returns a CANCEL function (a closure over the handle) rather than leaking a raw id — this is the clean pattern for effects that must be torn down (React cleanup, aborting polling). Second, accuracy: setInterval schedules callbacks every N ms measured from each fire, but the callback's own execution time and event-loop congestion cause DRIFT — if a tick's work takes 20ms, subsequent ticks accumulate lateness, and if the tab is throttled, setInterval can even queue up back-to-back catch-up calls. The fix is a self-correcting timer built from recursive setTimeout: record the intended next fire time, and after each tick compute the delay to the NEXT scheduled instant (`expected += period; delay = Math.max(0, expected - Date.now())`) so errors don't compound — the schedule stays anchored to absolute time rather than relative gaps. Recursive setTimeout also guarantees the previous callback FINISHED before the next is scheduled (no overlap/pile-up), unlike setInterval. Return a cancel that calls clearTimeout on the pending handle and sets a stopped flag so an in-flight tick won't reschedule. Always clear timers on teardown to avoid callbacks firing against unmounted state (a common React memory-leak/'setState on unmounted' bug).
Polling with cleanup, animation/clock loops needing accuracy, and any repeating effect that must be reliably cancelled.
O(1) per tick; no drift accumulation.