|
| 1 | +type DebouncedCallback = { |
| 2 | + flush: () => void | unknown; |
| 3 | + cancel: () => void; |
| 4 | + (): void | unknown; |
| 5 | +}; |
| 6 | +type CallbackFunction = () => unknown; |
| 7 | +type DebounceOptions = { maxWait?: number }; |
| 8 | + |
| 9 | +/** |
| 10 | + * Heavily simplified debounce function based on lodash.debounce. |
| 11 | + * |
| 12 | + * This function takes a callback function (@param fun) and delays its invocation |
| 13 | + * by @param wait milliseconds. Optionally, a maxWait can be specified in @param options, |
| 14 | + * which ensures that the callback is invoked at least once after the specified max. wait time. |
| 15 | + * |
| 16 | + * @param func the function whose invocation is to be debounced |
| 17 | + * @param wait the minimum time until the function is invoked after it was called once |
| 18 | + * @param options the options object, which can contain the `maxWait` property |
| 19 | + * |
| 20 | + * @returns the debounced version of the function, which needs to be called at least once to start the |
| 21 | + * debouncing process. Subsequent calls will reset the debouncing timer and, in case @paramfunc |
| 22 | + * was already invoked in the meantime, return @param func's return value. |
| 23 | + * The debounced function has two additional properties: |
| 24 | + * - `flush`: Invokes the debounced function immediately and returns its return value |
| 25 | + * - `cancel`: Cancels the debouncing process and resets the debouncing timer |
| 26 | + */ |
| 27 | +export function debounce(func: CallbackFunction, wait: number, options?: DebounceOptions): DebouncedCallback { |
| 28 | + let callbackReturnValue: unknown; |
| 29 | + |
| 30 | + let timerId: ReturnType<typeof setTimeout> | undefined; |
| 31 | + let maxTimerId: ReturnType<typeof setTimeout> | undefined; |
| 32 | + |
| 33 | + const maxWait = options && options.maxWait ? Math.max(options.maxWait, wait) : 0; |
| 34 | + |
| 35 | + function invokeFunc(): unknown { |
| 36 | + cancelTimers(); |
| 37 | + callbackReturnValue = func(); |
| 38 | + return callbackReturnValue; |
| 39 | + } |
| 40 | + |
| 41 | + function cancelTimers(): void { |
| 42 | + timerId !== undefined && clearTimeout(timerId); |
| 43 | + maxTimerId !== undefined && clearTimeout(maxTimerId); |
| 44 | + timerId = maxTimerId = undefined; |
| 45 | + } |
| 46 | + |
| 47 | + function flush(): unknown { |
| 48 | + if (timerId !== undefined || maxTimerId !== undefined) { |
| 49 | + return invokeFunc(); |
| 50 | + } |
| 51 | + return callbackReturnValue; |
| 52 | + } |
| 53 | + |
| 54 | + function debounced(): unknown { |
| 55 | + if (timerId) { |
| 56 | + clearTimeout(timerId); |
| 57 | + } |
| 58 | + timerId = setTimeout(invokeFunc, wait); |
| 59 | + |
| 60 | + if (maxWait && maxTimerId === undefined) { |
| 61 | + maxTimerId = setTimeout(invokeFunc, maxWait); |
| 62 | + } |
| 63 | + |
| 64 | + return callbackReturnValue; |
| 65 | + } |
| 66 | + |
| 67 | + debounced.cancel = cancelTimers; |
| 68 | + debounced.flush = flush; |
| 69 | + return debounced; |
| 70 | +} |
0 commit comments