debounce()
debounce<
Args,Context>(func,wait,immediate?): (this, ...args) =>void&object
Creates a debounced function that delays execution until after a wait period.
Type Parametersβ
Args: Args extends unknown[]β
The argument types of the function.
Context: Context = unknownβ
The type of this context.
Parametersβ
func: (this, ...args) => voidβ
The function to debounce.
wait: numberβ
The number of milliseconds to delay.
immediate?: boolean = falseβ
If true, trigger on leading edge instead of trailing edge.
Returns: (this, ...args) => void & objectβ
The debounced function with cancel() and flush() methods.
Throwsβ
RangeError If wait is negative or not finite.
Sinceβ
2.0.0
Performanceβ
Clears previous timeout on each call to prevent multiple pending executions.
Also known asβ
debounce (Lodash, es-toolkit, Remeda, Radashi, Modern Dash) Β· β (Ramda, Effect, Antfu)
Exampleβ
// Trailing edge (default)
const search = debounce((query: string) => {
console.log(`Searching: ${query}`);
}, 300);
search('abc'); // After 300ms: "Searching: abc"
// Leading edge
const save = debounce(() => console.log('Saved'), 1000, true);
save(); // Immediately: "Saved"
// Cancel pending
search.cancel();
// Force immediate execution
search('urgent');
search.flush(); // Immediately: "Searching: urgent"
How it works?β
Debounce delays execution until the user stops calling for a specified duration. Each call resets the timer β only the last call triggers the function.
Debounce vs Throttleβ
Debounce waits for silence β resets on every call. Throttle enforces rhythm β fires at fixed intervals.
| Debounce | Throttle | |
|---|---|---|
| Fires | Once, after silence | Periodically |
| Best for | Search input | Scroll events |
Use Casesβ
Optimize search input performance πβ
Delay search execution until user stops typing to reduce API calls and improve performance. Essential for search interfaces and autocomplete functionality.
const search = debounce((query) => {
api.search(query);
}, 300);
// User types "hello"... only one API call after 300ms
input.addEventListener("input", (e) => search((e.target as HTMLInputElement).value));
Delay API calls until idle πβ
Delay API requests until user stops interacting to prevent server overload. Critical for API optimization and reducing unnecessary network traffic.
const saveData = debounce((data) => {
api.save(data);
}, 1000);
// Save only after user stops editing for 1 second
editor.on("change", (data) => saveData(data));
Prevent rapid button clicksβ
Debounce button clicks to prevent accidental multiple submissions. Useful for form submissions and user interface interactions.
const submitForm = debounce(() => {
form.submit();
}, 500, true); // Immediate = true
// Only first click triggers action
button.addEventListener("click", submitForm);
Rate-limit WebSocket messages in real-time appsβ
Debounce high-frequency WebSocket messages to prevent UI overload. Critical for trading platforms, live dashboards, and real-time collaboration tools.
// Debounce price updates from trading WebSocket
const handlePriceUpdate = debounce((priceData) => {
updateTradingChart(priceData);
recalculatePortfolioValue(priceData);
}, 100);
websocket.on("price_update", (data) => {
handlePriceUpdate(data);
});
// For live chat typing indicators
const sendTypingIndicator = debounce(() => {
socket.emit("user_typing", { roomId, userId });
}, 500);
messageInput.addEventListener("input", sendTypingIndicator);
Auto-save documents while editingβ
Save document or form content automatically after the user stops typing. Essential for rich text editors, note-taking apps, and any "Google Docs-like" experience.
const autoSave = debounce(async (content: string) => {
await api.saveDraft({ content, updatedAt: Date.now() });
showSaveIndicator("Saved");
}, 2000);
editor.on("change", (content) => {
showSaveIndicator("Saving...");
autoSave(content);
});
Debounce filter panel changesβ
Wait for the user to finish adjusting filters before fetching results. Essential for faceted search with multiple filter controls (sliders, checkboxes).
const applyFilters = debounce(async (filters: FilterState) => {
const results = await api.searchProducts(filters);
renderProductGrid(results);
updateFilterCounts(results.facets);
}, 400);
priceSlider.addEventListener("input", (e) => {
applyFilters({ ...currentFilters, priceRange: parseRange(e) });
});
categoryCheckbox.addEventListener("change", () => {
applyFilters({ ...currentFilters, categories: getCheckedCategories() });
});
Delay window resize recalculationsβ
Recalculate chart dimensions or complex layouts only after resizing stops. Critical for chart libraries and responsive canvas elements.
const recalcLayout = debounce(() => {
const { width, height } = container.getBoundingClientRect();
chart.resize(width, height);
chart.redraw();
}, 250);
window.addEventListener("resize", recalcLayout);
Trigger infinite scroll loadingβ
Load the next page of content when the user stops scrolling near the bottom. Essential for social feeds, product listings, and content-heavy pages.
const checkInfiniteScroll = debounce(() => {
const { scrollTop, scrollHeight, clientHeight } = container;
const nearBottom = scrollTop + clientHeight >= scrollHeight - 200;
if (nearBottom && !isLoading) {
loadNextPage();
}
}, 150);
container.addEventListener("scroll", checkInfiniteScroll);
Debounce drag-end to finalize drop positionβ
Wait for the user to settle on a drop position before committing the reorder. Perfect for sortable lists and kanban boards where rapid dragging occurs.
const finalizeDrop = debounce((targetIndex: number) => {
commitReorder(draggedItem, targetIndex);
clearDropIndicators();
saveOrder();
}, 200);
sortableList.addEventListener("dragover", (e) => {
const targetIndex = getInsertIndex(e.clientY);
showDropIndicator(targetIndex);
finalizeDrop(targetIndex);
});
Debounce content observer mutationsβ
Debounce MutationObserver callbacks to batch rapid DOM changes. Essential for CDK-style ContentObserver that watches for content changes in components.
const observeContent = (element: HTMLElement, callback: () => void) => {
const debouncedCallback = debounce(callback, 50);
const observer = new MutationObserver(debouncedCallback);
observer.observe(element, {
childList: true,
subtree: true,
characterData: true,
});
return () => observer.disconnect();
};
// Watch for content changes to recalculate overlay position
const disconnect = observeContent(triggerElement, () => {
repositionOverlay();
});
Debounce container resize for responsive componentsβ
Debounce ResizeObserver callbacks to avoid layout thrashing during resize. Critical for CDK-style responsive components that adapt to container size.
const observeResize = (element: HTMLElement, callback: (rect: DOMRectReadOnly) => void) => {
const debouncedCallback = debounce((entries: ResizeObserverEntry[]) => {
const entry = entries[entries.length - 1];
callback(entry.contentRect);
}, 100);
const observer = new ResizeObserver(debouncedCallback);
observer.observe(element);
return () => observer.disconnect();
};
// Adapt component layout based on container width
observeResize(container, (rect) => {
const layout = rect.width < 400 ? "compact" : "full";
component.setLayout(layout);
});
Detect element overflow after content changesβ
Check if text overflows its container after dynamic content updates. Essential for showing "..." tooltips or "Show more" buttons only when needed.
const checkOverflow = debounce(() => {
textElements.forEach((el) => {
const isOverflowing = el.scrollWidth > el.clientWidth;
el.classList.toggle("has-tooltip", isOverflowing);
el.title = isOverflowing ? el.textContent ?? "" : "";
});
}, 200);
// Re-check after content changes or window resize
window.addEventListener("resize", checkOverflow);