How do you implement throttle from scratch?
Throttle limits a function to run at most once per time interval, however often it's called.
The approach:
Track whether you're in a "cooldown" period (a flag or last-run timestamp).
On a call, if outside the cooldown, run the function and start the cooldown.
Ignore calls during the cooldown; reset when it ends.
Versus debounce: throttle runs on a steady schedule, while debounce waits for silence.
function throttle(fn, interval) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
fn.apply(this, args);
}
};
}
// Usage: scroll handler fires at most every 100ms
const onScroll = throttle(() => {
console.log('Scroll position:', window.scrollY);
}, 100);
window.addEventListener('scroll', onScroll);
// Throttle with trailing call (ensures last call executes)
function throttleTrailing(fn, interval) {
let lastTime = 0;
let timer = null;
return function(...args) {
const now = Date.now();
const remaining = interval - (now - lastTime);
if (remaining <= 0) {
clearTimeout(timer);
lastTime = now;
fn.apply(this, args);
} else if (!timer) {
timer = setTimeout(() => {
lastTime = Date.now();
timer = null;
fn.apply(this, args);
}, remaining);
}
};
}Throttle checks elapsed time since last execution. If >= interval, execute immediately and update lastTime.
If not, the call is dropped. The trailing variant schedules a final execution for the remaining time — ensuring the last event is not lost (important for scroll position accuracy).
Simpler than debounce: just check elapsed time. Know the difference from debounce: throttle = regular rate, debounce = wait for pause.
The trailing variant (ensures last call fires) is the advanced follow-up. Show usage with scroll events.