Write throttle(fn, limit): fn runs at most once per `limit` ms, no matter how often it's called.
The difference from debounce, and getting the leading-edge timing right.
Track whether we're in a cooldown. On call, if not cooling down, run immediately and start a timer that re-opens the gate. Calls during the cooldown are ignored (this is the leading-edge variant). Contrast: debounce resets its timer on every call; throttle enforces a steady maximum rate. Two implementation styles worth knowing: the timestamp style (compare Date.now() to the last-run time — simple, leading-edge) and the timer style shown here. The subtle bug in the naive leading-edge version is that the LAST call during a cooldown is dropped, so the UI can end up stale (e.g. the final scroll position never handled); production throttles (lodash) therefore also fire a trailing call with the most recent args when the interval ends. Rule of thumb: throttle for continuous streams where you want regular sampling (scroll, mousemove, resize, drag); debounce for bursts where only the final state matters (typeahead, autosave).
scroll/mousemove/resize where you want regular updates but not on every pixel; rate-limiting API calls.