Limit high-frequency event handling
A mouse-move handler fires rapidly while updating a visual position.
JS Puzzles · Const
throttle(f, ms) creates a wrapper that invokes f at most once per interval. It runs the first call immediately and later forwards the latest call after the cooldown.
A throttle decorator limits a wrapped function to at most one execution per interval. The first call executes immediately; calls during the cooldown replace the saved arguments and context, and a timer later replays the latest saved invocation.
function throttle(f, ms)| Name | Type | Description |
|---|---|---|
f | Function | The function whose calls should be rate-limited. |
ms | number | The cooldown duration in milliseconds. |
A wrapper function that invokes the original function at most once per ms milliseconds.
function throttle(func, ms) {
let isThrottled = false;
let savedArgs;
let savedThis;
function wrapper(...args) {
if (isThrottled) {
savedArgs = args;
savedThis = this;
return;
}
func.apply(this, args);
isThrottled = true;
setTimeout(() => {
isThrottled = false;
if (savedArgs) {
wrapper.apply(savedThis, savedArgs);
savedArgs = savedThis = null;
}
}, ms);
}
return wrapper;
}
const log = throttle(value => console.log(value), 100);
log(1);
log(2);The wrapper runs immediately, ignores further calls during the interval while retaining the latest call, then replays that call after the cooldown.
A mouse-move handler fires rapidly while updating a visual position.
A continuously changing value must be reflected without running work on every update.
A costly operation should not execute more often than a chosen interval.
throttle(f, ms) returns a wrapper that calls f no more than once during each ms interval. The first wrapper call runs f immediately and starts the cooldown; later calls are retained until the interval ends.
Follow-up: How does the implementation handle calls during the cooldown? It stores the latest arguments and this, then invokes the wrapper with them when the timer expires.
The wrapper uses a boolean cooldown flag. A timer clears that flag after ms milliseconds and, if a call was saved, re-enters the wrapper to process the latest saved invocation.
Follow-up: Why use a timer rather than polling? The timer schedules the transition at the end of the cooldown without repeatedly checking elapsed time.
this and arguments be preserved?The wrapper must forward the original receiver and arguments, typically by applying the function with the saved context and argument list. Otherwise methods can lose their this value, and parameter data can be lost.
Follow-up: What behavior differs from debouncing? Throttling permits regular execution during sustained calls, whereas debouncing waits for a quiet period and favors the final result.
debouncesetTimeoutthisFunction.prototype.apply