intermediate 5 min read
Debounce & Throttle Internals
Debounce delays execution until a burst of events pauses for N milliseconds; Throttle guarantees execution at most once every N milliseconds during continuous event streams.
Why it matters in interviews
Both techniques prevent event handler overloading on high-frequency DOM events (resize, scroll, keydown), protecting 60fps performance and preventing API rate-limit abuse.
Visual & Interactive Explanation
Debounce vs Throttle Execution Comparison
Debounce
Execute after quiet period
Trigger BehaviorResets timer on each call
Execution Count1 execution at the end of burst
Best Use CaseSearch typeahead, form autosave
Strengths
- •Minimizes network calls
- •Guarantees user stopped typing
Trade-offs
- •Delayed initial feedback if trailing only
Throttle
Execute at regular intervals
Trigger BehaviorCapped to 1 execution per interval
Execution CountRegular cadence during continuous action
Best Use CaseScroll listeners, window resizing, gaming
Strengths
- •Continuous responsive feedback
- •Predictable cadence
Trade-offs
- •May trigger more times than debounce
Code Examples & Implementation
Production TypeScript Debounce Implementation
function debounce<T extends (...args: any[]) => any>(
fn: T,
delay: number
): ((...args: Parameters<T>) => void) & { cancel: () => void } {
let timer: ReturnType<typeof setTimeout> | null = null;
const debounced = function (this: any, ...args: Parameters<T>) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
};
debounced.cancel = () => {
if (timer) {
clearTimeout(timer);
timer = null;
}
};
return debounced;
}Production Timestamp Throttle Implementation
function throttle<T extends (...args: any[]) => any>(
fn: T,
limit: number
): (...args: Parameters<T>) => void {
let lastRan = 0;
let timer: ReturnType<typeof setTimeout> | null = null;
return function (this: any, ...args: Parameters<T>) {
const now = Date.now();
if (now - lastRan >= limit) {
fn.apply(this, args);
lastRan = now;
} else {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
lastRan = Date.now();
timer = null;
}, limit - (now - lastRan));
}
};
}Interview-Ready Answers
Debounce resets a timer on every trigger and only runs after inactivity (e.g. search inputs). Throttle limits execution to a fixed periodic interval (e.g. infinite scroll, resize).
Official Documentation & Specifications
Follow-up Questions & Deep Dives
🎯 Code Output — What Does This Print?
Question 1medium
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
const log = debounce(console.log, 100);
log("a");
log("b");
log("c");
// After 100ms, what is printed?Question 2hard
function throttle(fn, limit) {
let inThrottle = false;
return function(...args) {
if (!inThrottle) {
fn(...args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
const log = throttle(console.log, 1000);
log("a"); // t=0ms
log("b"); // t=50ms
log("c"); // t=100ms
// What is printed immediately?Question 3tricky
let count = 0;
const inc = () => { count++; console.log(count); };
const debounced = (() => {
let timer;
return () => {
clearTimeout(timer);
timer = setTimeout(inc, 0);
};
})();
debounced();
debounced();
debounced();
// After microtask+macrotask drain:Common Mistakes & Anti-Patterns
- Re-creating debounced functions inside React component bodies on every render without useMemo or useCallback
- Failing to clean up timers on unmount, triggering state updates on unmounted components
- Losing `this` context or arguments inside the timer callback
Real-World Architectural Scenario
In a React autosave editor, typing triggers a debounce save. But if the user suddenly navigates away, uncompleted edits are lost. How do you design this?
Rate Your Readiness
Rate how comfortably you can explain this in an interview to update your global readiness gauge:
Rate your confidence: