Throttle and Debounce Functions in TypeScript
A user scrolls a page. Your scroll handler fires 300 times in two seconds. Each one triggers a layout calculation. The page stutters. The user leaves.
7 May 2024

A user scrolls a page. Your scroll handler fires 300 times in two seconds. Each one triggers a layout calculation. The page stutters. The user leaves.
Throttle and debounce solve this. They both limit how often a function runs, but they do it differently.
Throttle: "Run at most once every N ms"
Throttle guarantees the function executes at a steady rate. No matter how many times the event fires, the function runs at most once per interval.
Good for: scroll position tracking, resize handlers, rate-limited API calls.
const throttle = (callback: () => void, delay: number) => {
let waiting = false;
return () => {
if (!waiting) {
callback();
waiting = true;
setTimeout(() => {
waiting = false;
}, delay);
}
};
};
window.addEventListener(
"scroll",
throttle(() => {
console.log("Scrolled!");
}, 200)
);
Debounce: "Wait until they stop, then run once"
Debounce waits for silence. Every time the event fires, it resets the timer. The function only runs after the user stops triggering it for N milliseconds.
Good for: search-as-you-type, form validation, window resize end detection.
const debounce = (callback: () => void, delay: number) => {
let timeout: ReturnType<typeof setTimeout> | undefined;
return () => {
clearTimeout(timeout);
timeout = setTimeout(() => {
callback();
}, delay);
};
};
const searchInput = document.getElementById("search-input") as HTMLInputElement;
searchInput.addEventListener(
"input",
debounce(() => {
console.log("Searching...");
}, 300)
);
Which one?
Throttle when you need consistent updates during an ongoing action (scroll position, mouse move).
Debounce when you only care about the final state (search input, resize end).
The cost of getting it wrong: throttle where you need debounce means wasted work. Debounce where you need throttle means a laggy UI that only updates after the user stops interacting.
Also worth reading: Optimizing JavaScript Event Listeners for Performance