throttle()
throttle<
Args,Context>(func,wait): (this, ...args) =>void&object
Creates a throttled function that invokes at most once per wait period.
Executes on leading edge (immediately) and trailing edge (after 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 throttle.
wait: numberβ
The number of milliseconds to throttle invocations.
Returns: (this, ...args) => void & objectβ
The throttled function with a cancel() method.
Throwsβ
RangeError If wait is negative or not finite.
Sinceβ
2.0.0
Noteβ
Call .cancel() to clear any pending execution.
Performanceβ
Uses timestamp comparison to avoid unnecessary setTimeout calls. Stores latest args/context for trailing edge execution.
Also known asβ
throttle (Lodash, es-toolkit, Remeda, Radashi, Modern Dash) Β· β (Ramda, Effect, Antfu)
Exampleβ
// Scroll handler
const handleScroll = throttle(() => {
console.log('Scrolling...');
}, 200);
window.addEventListener('scroll', handleScroll);
// With arguments
const logMouse = throttle((x: number, y: number) => {
console.log('Mouse:', x, y);
}, 100);
document.addEventListener('mousemove', (e) => {
logMouse(e.clientX, e.clientY);
});
// Cancel pending
logMouse.cancel();
How it works?β
Throttle enforces a maximum execution rate β the function fires at most once per interval. Calls during the cooldown are ignored, ensuring consistent rhythm regardless of input frequency.
Throttle vs Debounceβ
Throttle enforces rhythm β fires at fixed intervals regardless of call frequency. Debounce waits for silence β resets on every call.
| Throttle | Debounce | |
|---|---|---|
| Fires | Periodically | Once, after silence |
| Best for | Scroll events | Search input |
Use Casesβ
Limit function execution frequency πβ
Control the rate of function execution to prevent performance issues. Essential for performance optimization and preventing excessive resource usage.
const logScroll = throttle(() => {
console.log("Scrolled!");
}, 100);
window.addEventListener("scroll", logScroll);
Optimize scroll and resize eventsβ
Limit the frequency of scroll and resize event handlers. Critical for responsive design and scroll-based UI. Limit animation updates to maintain smooth performance. Essential for smooth animations and visual effects.
const updateCanvas = throttle((mouseX: number, mouseY: number) => {
ctx.clearRect(0, 0, width, height);
drawParticles(mouseX, mouseY);
}, 33); // Cap at ~30fps
canvas.addEventListener("mousemove", (e) => updateCanvas(e.offsetX, e.offsetY));
Rate-limit ticket purchases during high-demand salesβ
Throttle ticket purchase requests to ensure fair access during flash sales. Essential for concert ticketing, sports events, and limited availability sales.
// Throttle purchase attempts to 1 per second per user
const attemptPurchase = throttle(async (ticketId, userId) => {
const result = await ticketingAPI.purchase({ ticketId, userId });
if (result.success) {
showConfirmation(result.ticket);
} else {
showWaitlistOption();
}
}, 1000);
buyButton.addEventListener("click", () => {
attemptPurchase(selectedTicketId, currentUserId);
});
// Throttle seat selection updates to reduce server load
const updateSeatSelection = throttle((seats) => {
api.reserveSeats(seats);
updateSeatMap(seats);
}, 500);
seatPicker.on("selection_change", updateSeatSelection);
Smooth canvas drawing with pointer trackingβ
Throttle pointer move events to control brush stroke density on a canvas. Essential for drawing apps, signature pads, and annotation tools.
const drawStroke = throttle((x: number, y: number) => {
ctx.lineTo(x, y);
ctx.stroke();
}, 16); // ~60fps
canvas.addEventListener("pointermove", (e) => {
if (isDrawing) {
drawStroke(e.offsetX, e.offsetY);
}
});
Limit gesture recognition updates on mobileβ
Throttle touch events during pinch-to-zoom or swipe gestures. Critical for mobile web apps handling complex multi-touch interactions.
const handlePinch = throttle((scale: number, centerX: number, centerY: number) => {
applyZoom(scale, centerX, centerY);
updateMinimap();
}, 50);
element.addEventListener("touchmove", (e) => {
if (e.touches.length === 2) {
const scale = getDistance(e.touches[0], e.touches[1]) / initialDistance;
const center = getMidpoint(e.touches[0], e.touches[1]);
handlePinch(scale, center.x, center.y);
}
});
Track drag-and-drop position updatesβ
Throttle drag events to update drop zone indicators without overwhelming the renderer. Essential for sortable lists, kanban boards, and drag-and-drop builders.
const onDrag = throttle((e: DragEvent) => {
const dropTarget = getDropTarget(e.clientX, e.clientY);
highlightDropZone(dropTarget);
updateDragPreview(e.clientX, e.clientY);
}, 32); // ~30fps is enough for visual feedback
draggable.addEventListener("drag", onDrag);
Update sticky header shadow on scrollβ
Add or remove a shadow on a sticky header based on scroll position. Perfect for app shells and dashboard layouts with fixed navigation.
const updateHeaderShadow = throttle(() => {
const scrolled = window.scrollY > 0;
header.classList.toggle("elevated", scrolled);
}, 100);
window.addEventListener("scroll", updateHeaderShadow);
Throttle pull-to-refresh gesture detectionβ
Limit pull-to-refresh progress updates during touch drag. Critical for mobile web apps implementing native-like pull-to-refresh.
const onPullProgress = throttle((distance: number) => {
const progress = clamp(distance / PULL_THRESHOLD, 0, 1);
refreshIndicator.style.transform = `translateY(${distance}px) rotate(${progress * 360}deg)`;
refreshIndicator.style.opacity = String(progress);
}, 16);
container.addEventListener("touchmove", (e) => {
if (isPulling) {
const distance = e.touches[0].clientY - pullStartY;
onPullProgress(distance);
}
});
Dispatch scroll events to multiple overlay subscribersβ
Throttle a centralized scroll dispatcher that notifies all active overlays. Essential for CDK-style ScrollDispatcher that manages overlay repositioning.
const activeOverlays: Array<{ reposition: () => void }> = [];
const scrollDispatcher = throttle(() => {
activeOverlays.forEach((overlay) => overlay.reposition());
}, 16);
// Single scroll listener for all overlays
window.addEventListener("scroll", scrollDispatcher, { passive: true });
// Overlays register/unregister
const registerOverlay = (overlay: { reposition: () => void }) => {
activeOverlays.push(overlay);
return () => {
const idx = activeOverlays.indexOf(overlay);
if (idx >= 0) activeOverlays.splice(idx, 1);
};
};
Limit virtual scroll position recalculationsβ
Throttle scroll handler in a virtual scroll implementation to reduce layout thrashing. Essential for rendering large lists (10k+ items) without performance degradation.
const onVirtualScroll = throttle(() => {
const scrollTop = container.scrollTop;
const startIndex = Math.floor(scrollTop / ROW_HEIGHT);
const endIndex = startIndex + Math.ceil(container.clientHeight / ROW_HEIGHT);
renderVisibleRows(startIndex, endIndex);
updateScrollbar(scrollTop);
}, 16); // 60fps
container.addEventListener("scroll", onVirtualScroll);