| |
| |
| |
|
|
| |
| |
| |
| export interface DebounceOptions { |
| |
| |
| |
| |
| leading?: boolean; |
|
|
| |
| |
| |
| |
| trailing?: boolean; |
|
|
| |
| |
| |
| |
| maxWait?: number; |
| } |
|
|
| |
| |
| |
| export interface DebouncedFunction<T extends (...args: unknown[]) => unknown> { |
| |
| |
| |
| (...args: Parameters<T>): void; |
|
|
| |
| |
| |
| cancel(): void; |
|
|
| |
| |
| |
| flush(): void; |
|
|
| |
| |
| |
| pending(): boolean; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function debounce<T extends (...args: unknown[]) => unknown>( |
| fn: T, |
| wait: number, |
| options: DebounceOptions = {} |
| ): DebouncedFunction<T> { |
| const { leading = false, trailing = true, maxWait } = options; |
|
|
| let timeoutId: ReturnType<typeof setTimeout> | null = null; |
| let maxTimeoutId: ReturnType<typeof setTimeout> | null = null; |
| let lastArgs: Parameters<T> | null = null; |
| let lastCallTime: number | null = null; |
| let lastInvokeTime = 0; |
|
|
| |
| if (maxWait !== undefined && maxWait < wait) { |
| throw new Error('maxWait must be greater than or equal to wait'); |
| } |
|
|
| function invokeFunc(): void { |
| const args = lastArgs; |
| lastArgs = null; |
| lastInvokeTime = Date.now(); |
|
|
| if (args !== null) { |
| fn(...args); |
| } |
| } |
|
|
| function shouldInvoke(time: number): boolean { |
| const timeSinceLastCall = lastCallTime === null ? 0 : time - lastCallTime; |
| const timeSinceLastInvoke = time - lastInvokeTime; |
|
|
| |
| return ( |
| lastCallTime === null || |
| timeSinceLastCall >= wait || |
| timeSinceLastCall < 0 || |
| (maxWait !== undefined && timeSinceLastInvoke >= maxWait) |
| ); |
| } |
|
|
| function timerExpired(): void { |
| const time = Date.now(); |
|
|
| if (shouldInvoke(time)) { |
| trailingEdge(); |
| return; |
| } |
|
|
| |
| const timeSinceLastCall = lastCallTime === null ? 0 : time - lastCallTime; |
| const timeSinceLastInvoke = time - lastInvokeTime; |
| const timeWaiting = wait - timeSinceLastCall; |
|
|
| const remainingWait = |
| maxWait !== undefined ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting; |
|
|
| timeoutId = setTimeout(timerExpired, remainingWait); |
| } |
|
|
| function trailingEdge(): void { |
| timeoutId = null; |
|
|
| if (trailing && lastArgs !== null) { |
| invokeFunc(); |
| } |
|
|
| lastArgs = null; |
| } |
|
|
| function leadingEdge(time: number): void { |
| lastInvokeTime = time; |
|
|
| |
| timeoutId = setTimeout(timerExpired, wait); |
|
|
| |
| if (leading) { |
| invokeFunc(); |
| } |
| } |
|
|
| function cancel(): void { |
| if (timeoutId !== null) { |
| clearTimeout(timeoutId); |
| timeoutId = null; |
| } |
| if (maxTimeoutId !== null) { |
| clearTimeout(maxTimeoutId); |
| maxTimeoutId = null; |
| } |
| lastArgs = null; |
| lastCallTime = null; |
| lastInvokeTime = 0; |
| } |
|
|
| function flush(): void { |
| if (timeoutId !== null) { |
| invokeFunc(); |
| cancel(); |
| } |
| } |
|
|
| function pending(): boolean { |
| return timeoutId !== null; |
| } |
|
|
| function debounced(...args: Parameters<T>): void { |
| const time = Date.now(); |
| const isInvoking = shouldInvoke(time); |
|
|
| lastArgs = args; |
| lastCallTime = time; |
|
|
| if (isInvoking) { |
| if (timeoutId === null) { |
| leadingEdge(time); |
| return; |
| } |
|
|
| |
| if (maxWait !== undefined) { |
| timeoutId = setTimeout(timerExpired, wait); |
| invokeFunc(); |
| return; |
| } |
| } |
|
|
| if (timeoutId === null) { |
| timeoutId = setTimeout(timerExpired, wait); |
| } |
| } |
|
|
| debounced.cancel = cancel; |
| debounced.flush = flush; |
| debounced.pending = pending; |
|
|
| return debounced; |
| } |
|
|
| |
| |
| |
| export interface ThrottleOptions { |
| |
| |
| |
| |
| leading?: boolean; |
|
|
| |
| |
| |
| |
| trailing?: boolean; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function throttle<T extends (...args: unknown[]) => unknown>( |
| fn: T, |
| wait: number, |
| options: ThrottleOptions = {} |
| ): DebouncedFunction<T> { |
| const { leading = true, trailing = true } = options; |
|
|
| return debounce(fn, wait, { |
| leading, |
| trailing, |
| maxWait: wait, |
| }); |
| } |
|
|