Loading...
Loading...
By continuing to use the platform, you accept the terms of the Privacy Policy and the use of cookies.
Debounce and Throttle are performance optimization techniques that control the frequency of function execution, especially when handling events that can be triggered very frequently (scroll, resize, input, etc.).
Debounce delays function execution until a certain amount of time has passed since the last invocation.
function debounce(func, delay) {
let timeoutId;
return function(...args) {
// Clear previous timer
clearTimeout(timeoutId);
// Set new timer
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// Usage
const searchInput = document.querySelector('#search');
const handleSearch = debounce((event) => {
console.log('Search:', event.target.value);
// API request
}, 500);
searchInput.addEventListener('input', handleSearch);
delay milliseconds have passed since the last callResult:
If user types "javascript", instead of 10 requests (one per letter), only one request will be sent 500ms after user finishes typing.
Throttle ensures the function executes no more than once per specified time interval.
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => {
inThrottle = false;
}, limit);
}
};
}
// Usage
const handleScroll = throttle(() => {
console.log('Scroll position:', window.scrollY);
// Check if reached end of page
}, 1000);
window.addEventListener('scroll', handleScroll);
inThrottle flag is set to truelimit milliseconds passResult:
If user scrolls page rapidly, function will be called maximum once per second, instead of hundreds of times per second.
| Characteristic | Debounce | Throttle |
|---|---|---|
| Behavior | Executes after pause | Executes at intervals |
| First call | Delayed | Executes immediately |
| Frequent calls | All reset | Executes once per N ms |
| Use cases | Search, auto-save | Scroll, resize, tracking |
Imagine an event occurs 10 times per second:
Without optimization:
█ █ █ █ █ █ █ █ █ █ (10 calls)
With Debounce (500ms):
░ ░ ░ ░ ░ ░ ░ ░ ░ █ (1 call after pause)
With Throttle (500ms):
█ ░ ░ ░ ░ █ ░ ░ ░ ░ (2 calls at intervals)
function debounce(func, delay, { leading = false, trailing = true } = {}) {
let timeoutId;
return function(...args) {
const isInvokingLater = !timeoutId;
clearTimeout(timeoutId);
if (leading && isInvokingLater) {
func.apply(this, args);
}
timeoutId = setTimeout(() => {
if (trailing) {
func.apply(this, args);
}
timeoutId = null;
}, delay);
};
}
leading: true — call at the beginningtrailing: true — call at the end (default)function throttle(func, limit, { leading = true, trailing = true } = {}) {
let inThrottle;
let lastArgs;
return function(...args) {
if (!inThrottle) {
if (leading) {
func.apply(this, args);
}
inThrottle = true;
setTimeout(() => {
inThrottle = false;
if (trailing && lastArgs) {
func.apply(this, lastArgs);
lastArgs = null;
}
}, limit);
} else {
lastArgs = args;
}
};
}
In production, ready-made solutions are often used:
import { debounce, throttle } from 'lodash';
const debouncedSearch = debounce(searchFunction, 500);
const throttledScroll = throttle(scrollHandler, 1000);
const debouncedFn = debounce(someFunction, 1000);
// Cancel pending call
debouncedFn.cancel();
import { useCallback, useRef, useEffect } from 'react';
function useDebounce(callback, delay) {
const timeoutRef = useRef(null);
useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);
return useCallback((...args) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
callback(...args);
}, delay);
}, [callback, delay]);
}
// Usage in component
function SearchComponent() {
const handleSearch = useDebounce((value) => {
console.log('Searching:', value);
}, 500);
return (
<input
type="text"
onChange={(e) => handleSearch(e.target.value)}
/>
);
}
this context// Wrong
function debounce(func, delay) {
let timeoutId;
return function() {
clearTimeout(timeoutId);
timeoutId = setTimeout(func, delay); // lose this
};
}
// Correct
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay); // ✅
};
}
// Wrong in React
function Component() {
// Each render creates new function
const handler = debounce(() => {}, 500);
return <input onChange={handler} />;
}
// Correct
function Component() {
// Function created once
const handler = useMemo(
() => debounce(() => {}, 500),
[]
);
return <input onChange={handler} />;
}
this) and arguments in implementationIn Interviews:
Often asked to implement debounce/throttle from scratch and explain differences. It's important to understand not only implementation but also practical use cases.