Aller au contenu principal

throttle()

throttle<Args, Context>(func, wait): (this, ...args) => void & object

Creates a throttled function that invokes at most once per wait period.

remarque

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 vs Debounce comparison

ThrottleDebounce
FiresPeriodicallyOnce, after silence
Best forScroll eventsSearch 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);