Throttle
Capping how often a hot handler runs, so scroll, resize, and pointer events can keep updating without flooding the app.
Some events keep firing while the user keeps moving. Scroll, resize, and pointer movement can trigger dozens of callbacks per second. Throttle keeps the handler running at a steady maximum rate.
Throttle is a gate. It allows the first call, then blocks the rest until the interval passes. It fits scroll position updates, window resize work, and telemetry where intermediate values still matter.
function throttle(fn, delay = 300) {
let lastRun = 0;
return (...args) => {
const now = Date.now();
if (now - lastRun >= delay) {
lastRun = now;
fn(...args);
}
};
}
const reportScroll = throttle(() => {
console.log("scrollY:", window.scrollY);
}, 200);
window.addEventListener("scroll", reportScroll);The throttled handler still runs while the event stream is active. It just samples the stream at a controlled rate instead of reacting to every event.
const samplePointer = throttle((event) => {
console.log("pointer at", event.clientX, event.clientY);
}, 100);
window.addEventListener("pointermove", samplePointer);Use debounce when the final value is the only value that matters. Use throttle when the system still needs regular updates while the action is happening.
const saveDraft = debounce(() => {
console.log("save once typing stops");
}, 500);
const samplePosition = throttle(() => {
console.log("sample while the user keeps scrolling");
}, 250);In production
Throttle is safer than debounce for scroll and resize work, but the simple version above skips the trailing value. If the last update matters, use a throttle with trailing execution. For visual work tied to painting, prefer requestAnimationFrame so the browser can align the callback with the next frame.
Throttle limits the rate, so a hot event stream stays useful without overwhelming the page.