throttle
Creates a throttled version of a function.
1/**
2 * Creates a throttled version of a function.
3 *
4 * @param fn - Function to throttle.
5 * @param interval - Minimum time (ms) between calls.
6 * @param options - Optional config:
7 * - leading: Call on the leading edge (default: true)
8 * - trailing: Call on the trailing edge (default: true)
9 * @returns A throttled function with a cancel method.
10 */
11export function throttle<T extends (...args: any[]) => void>(
12 fn: T,
13 interval: number,
14 options: { leading?: boolean; trailing?: boolean } = {}
15): ((...args: Parameters<T>) => void) & { cancel: () => void } {
16 const { leading = true, trailing = true } = options;
17 let timeoutId: ReturnType<typeof setTimeout> | null = null;
18 let lastCallTime: number | null = null;
19 let savedArgs: Parameters<T> | null = null;
20
21 const throttled = (...args: Parameters<T>) => {
22 const now = Date.now();
23
24 if (lastCallTime === null && !leading) {
25 lastCallTime = now;
26 }
27
28 const remaining = interval - (now - (lastCallTime ?? 0));
29
30 savedArgs = args;
31
32 if (remaining <= 0 || lastCallTime === null) {
33 if (timeoutId) {
34 clearTimeout(timeoutId);
35 timeoutId = null;
36 }
37 fn(...args);
38 lastCallTime = now;
39 } else if (trailing && !timeoutId) {
40 timeoutId = setTimeout(() => {
41 timeoutId = null;
42 if (savedArgs) {
43 fn(...savedArgs);
44 lastCallTime = Date.now();
45 savedArgs = null;
46 }
47 }, remaining);
48 }
49 };
50
51 throttled.cancel = () => {
52 if (timeoutId) clearTimeout(timeoutId);
53 timeoutId = null;
54 lastCallTime = null;
55 savedArgs = null;
56 };
57
58 return throttled;
59}
Dual Edge Support (Leading & Trailing)
Offers fine-grained control over whether the function is called at the start, end, or both ends of a throttle interval.
Stateful Delay Management
Tracks call timing and arguments internally to handle pending trailing executions reliably.
Manual Cancellation
Exposes a
.cancel()
method for explicit control over cleanup, ideal for component unmounting or lifecycle hooks.Type-Safe and Flexible
Fully generic in TypeScript, preserving function argument and return types for improved type safety and autocomplete.
Performance Optimization
Prevents excessive invocations of high-frequency functions (like event handlers), improving UI responsiveness and CPU efficiency.
Tests | Examples
1jest.useFakeTimers();
2
3test('throttle - basic throttling', () => {
4 const fn = jest.fn();
5 const throttled = throttle(fn, 1000);
6
7 throttled();
8 throttled();
9 throttled();
10
11 expect(fn).toHaveBeenCalledTimes(1);
12
13 jest.advanceTimersByTime(1000);
14 throttled();
15
16 expect(fn).toHaveBeenCalledTimes(2);
17});
18
19test('throttle - leading: false, trailing: true', () => {
20 const fn = jest.fn();
21 const throttled = throttle(fn, 1000, { leading: false, trailing: true });
22
23 throttled();
24 throttled();
25 throttled();
26
27 expect(fn).toHaveBeenCalledTimes(0);
28
29 jest.advanceTimersByTime(1000);
30 expect(fn).toHaveBeenCalledTimes(1);
31});
32
33test('throttle - trailing: false, only leading', () => {
34 const fn = jest.fn();
35 const throttled = throttle(fn, 1000, { trailing: false });
36
37 throttled();
38 throttled();
39 throttled();
40
41 expect(fn).toHaveBeenCalledTimes(1);
42
43 jest.advanceTimersByTime(1000);
44 expect(fn).toHaveBeenCalledTimes(1);
45});
46
47test('throttle - cancel works', () => {
48 const fn = jest.fn();
49 const throttled = throttle(fn, 1000);
50
51 throttled();
52 throttled();
53
54 throttled.cancel();
55
56 jest.advanceTimersByTime(1000);
57 expect(fn).toHaveBeenCalledTimes(1); // trailing call canceled
58});
Common Use Cases
Scroll or Resize Event Handling
Throttle expensive layout recalculations or UI updates during frequent DOM events.
Mouse or Touch Movement Tracking
Limit position-based updates (e.g., drag-and-drop, drawing) to a reasonable rate.
Button or Action Debouncing
Prevent multiple rapid clicks or taps from triggering duplicate requests or transitions.
Auto-Save or Auto-Sync
Periodically save input changes without overwhelming the backend or local storage.
Analytics & Metrics Throttling
Reduce the frequency of metric collection or log submissions during high-activity sessions.