Throttle
Prompt
Implement a throttle function that takes a callback function and a delay in milliseconds, and returns a throttled version of it.
The returned function should forward all arguments to the original callback.
Playground
Unlike debounce which delays execution, throttle allows the first call to go through immediately. The challenge is preventing subsequent calls within the delay window.
You need a boolean flag that tracks whether the function
is currently in its "cooldown" period. While the flag is
true, new calls are ignored.
After executing the function, set the flag to true and
use setTimeout to flip it back to false after the
delay. New calls should only execute when the flag is
false.
Solution
Explanation
Throttling enforces a maximum number of times a function can be called over time.
Imagine a button that makes an API call when clicked. A user might click it 10 times in a second, but you only want to make one API call per second. Without throttling, every single click would fire an API call. Throttle lets the first click go through immediately, then ignores all subsequent clicks until the cooldown period is over.
The solution
function throttle(fn, delay) {
let isThrottled = false;
return function (...args) {
if (isThrottled) return;
// Save the context of 'this'
const context = this;
// Execute fn with the correct context and arguments
fn.apply(context, args);
isThrottled = true;
setTimeout(() => {
isThrottled = false;
}, delay);
};
}We use a boolean flag isThrottled that acts like a gate. When the throttled function is called:
-
If
isThrottledisfalse, the gate is open. We run the original function, then close the gate by settingisThrottled = true. We also start a timer that will reopen the gate afterdelaymilliseconds.You'll also notice we use
const context = this;alongsidefn.apply(context, args)here instead of justfn(...args). This is actually a super crucial detail! It ensures that the throttled function perfectly remembers itsthiscontext. This comes up a lot if you're attaching the throttled function to an object or a DOM element. By explicitly saving the context and usingapply(context, args), we're saying, "Make sure the original function gets exactly the same context and arguments." This extra polish really shows interviewers you've mastered the nuances of JavaScript. -
If
isThrottledistrue, the gate is closed. We do nothing and return immediately.
Once the setTimeout fires, isThrottled flips back to false, and the next call will go through again.
Debounce vs Throttle
These two are always asked together in interviews, so it's worth knowing the difference.
Debounce waits until the caller stops calling, then fires once. If you keep calling, it keeps resetting the timer and never fires until you're quiet for the full delay.
Throttle fires immediately on the first call, then ignores calls for the next delay milliseconds. It guarantees at most one execution per time window.
A practical way to remember: debounce is for search inputs (wait until the user finishes typing), throttle is for scroll handlers (fire at a steady rate while scrolling, but not on every pixel).
You'll often see throttle used with scroll events, resize handlers, and rate-limited button clicks. Any situation where you want steady, evenly-spaced executions rather than waiting for silence.