Debounce and throttle are two important techniques to control how often a function is executed in response to frequent events such as scroll, resize, mousemove, or keypress.
They help improve performance and provide a smoother user experience by reducing unnecessary function calls.
⚡ Performance Optimization ? Event Control ?️ Scroll & Input
Both debounce and throttle are usually implemented as higher-order functions: they take a function as input and return a new, wrapped version with extra behavior.
setTimeout, clearTimeout, and sometimes Date.now().apply and the rest/spread syntax.
function debounce(func, delay) {
let timer; // Stores the timeout ID between calls
return function(...args) {
clearTimeout(timer); // Reset timer on each call
timer = setTimeout(() => {
func.apply(this, args); // Run after user stops triggering events
}, delay);
};
}
window.addEventListener('resize', debounce(() => {
console.log('Resized!'); // Fires only after resizing ends
}, 300));
function throttle(func, limit) {
let lastFunc; // Timeout ID for a scheduled call
let lastRan; // Timestamp of the last execution
return function(...args) {
if (!lastRan) { // First call: run immediately
func.apply(this, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc); // Reset any pending execution
lastFunc = setTimeout(() => {
const now = Date.now();
if (now - lastRan >= limit) { // Only run if enough time has passed
func.apply(this, args);
lastRan = now;
}
}, limit - (Date.now() - lastRan)); // Wait remaining time in the limit window
}
};
}
window.addEventListener('scroll', throttle(() => {
console.log('Scrolling...'); // Throttled scroll handler
}, 500));
Imagine typing into a search box. With debounce set to 500ms:
Now imagine a scroll event with throttle set to 200ms:
func.apply(this, args)).console.log with DOM updates (like showing a counter) to visualize how often your functions actually run.