Problem Statement
What is debouncing in JavaScript?
Explanation
Debouncing delays function execution until after the user stops triggering the event for a specified time.
If the event keeps firing, the timer resets. The function only executes after the user stops for the delay period.
Debouncing is useful for search inputs, resize events, and text input validation where you want to wait until the user finishes.
It reduces the number of function calls significantly, improving performance.
Common use cases include autocomplete search, form validation, and window resize handlers.
Understanding debouncing is essential for optimizing event-heavy applications.
Code Solution
SolutionRead Only
// DEBOUNCE IMPLEMENTATION
function debounce(func, delay) {
let timeoutId;
return function(...args) {
// Clear previous timeout
clearTimeout(timeoutId);
// Set new timeout
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// HOW IT WORKS
// User types: a -> b -> c -> d
// Without debounce: 4 API calls (a, b, c, d)
// With debounce: 1 API call (after user stops typing)
// PRACTICAL EXAMPLE - Search input
const searchInput = document.getElementById('search');
function searchAPI(query) {
console.log('Searching for:', query);
// Make API call
}
// Without debounce (too many calls!)
searchInput.addEventListener('input', (e) => {
searchAPI(e.target.value); // Calls on every keystroke!
});
// With debounce (efficient)
const debouncedSearch = debounce(searchAPI, 500);
searchInput.addEventListener('input', (e) => {
debouncedSearch(e.target.value); // Only calls after 500ms pause
});
// WINDOW RESIZE EXAMPLE
function handleResize() {
console.log('Window resized to:', window.innerWidth);
// Expensive layout calculations
}
// Without debounce: hundreds of calls while resizing
window.addEventListener('resize', handleResize);
// With debounce: one call after resize stops
window.addEventListener('resize', debounce(handleResize, 250));
// FORM VALIDATION EXAMPLE
const emailInput = document.getElementById('email');
function validateEmail(email) {
console.log('Validating:', email);
// Validation logic
}
const debouncedValidate = debounce(validateEmail, 300);
emailInput.addEventListener('input', (e) => {
debouncedValidate(e.target.value);
});
// IMMEDIATE EXECUTION DEBOUNCE
function debounceImmediate(func, delay) {
let timeoutId;
return function(...args) {
const callNow = !timeoutId;
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
timeoutId = null;
}, delay);
if (callNow) {
func.apply(this, args);
}
};
}
// Executes immediately on first call,
// then waits for delay before allowing next execution
// CANCELABLE DEBOUNCE
function debounceWithCancel(func, delay) {
let timeoutId;
const debounced = function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
debounced.cancel = function() {
clearTimeout(timeoutId);
};
return debounced;
}
const debouncedFunc = debounceWithCancel(myFunction, 1000);
// Cancel if needed
debouncedFunc.cancel();
// REAL-WORLD SCENARIOS
// Autocomplete
const autocomplete = debounce(async (query) => {
const results = await fetch(`/api/search?q=${query}`);
displaySuggestions(await results.json());
}, 300);
// Scroll position saving
const saveScrollPosition = debounce(() => {
localStorage.setItem('scrollPos', window.scrollY);
}, 500);
window.addEventListener('scroll', saveScrollPosition);
// Button click protection
const handleSubmit = debounce(() => {
console.log('Form submitted');
// Submit form
}, 1000);
button.addEventListener('click', handleSubmit);