//#region src/index.ts const DEBOUNCE_DEFAULTS = { trailing: true }; /** Debounce functions @param fn - Promise-returning/async function to debounce. @param wait - Milliseconds to wait before calling `fn`. Default value is 25ms @returns A function that delays calling `fn` until after `wait` milliseconds have elapsed since the last time it was called. @example ``` import { debounce } from 'perfect-debounce'; const expensiveCall = async input => input; const debouncedFn = debounce(expensiveCall, 200); for (const number of [1, 2, 3]) { console.log(await debouncedFn(number)); } //=> 1 //=> 2 //=> 3 ``` */ function debounce(fn, wait = 25, options = {}) { options = { ...DEBOUNCE_DEFAULTS, ...options }; if (!Number.isFinite(wait)) throw new TypeError("Expected `wait` to be a finite number"); let leadingValue; let timeout; let resolveList = []; let currentPromise; let trailingArgs; const applyFn = (_this, args) => { currentPromise = _applyPromised(fn, _this, args); currentPromise.finally(() => { currentPromise = null; if (options.trailing && trailingArgs && !timeout) { const promise = applyFn(_this, trailingArgs); trailingArgs = null; return promise; } }); return currentPromise; }; const debounced = function(...args) { if (options.trailing) trailingArgs = args; if (currentPromise) return currentPromise; return new Promise((resolve) => { const shouldCallNow = !timeout && options.leading; clearTimeout(timeout); timeout = setTimeout(() => { timeout = null; const promise = options.leading ? leadingValue : applyFn(this, args); trailingArgs = null; for (const _resolve of resolveList) _resolve(promise); resolveList = []; }, wait); if (shouldCallNow) { leadingValue = applyFn(this, args); resolve(leadingValue); } else resolveList.push(resolve); }); }; const _clearTimeout = (timer) => { if (timer) { clearTimeout(timer); timeout = null; } }; debounced.isPending = () => !!timeout; debounced.cancel = () => { _clearTimeout(timeout); resolveList = []; trailingArgs = null; }; debounced.flush = () => { _clearTimeout(timeout); if (!trailingArgs || currentPromise) return; const args = trailingArgs; trailingArgs = null; return applyFn(this, args); }; return debounced; } async function _applyPromised(fn, _this, args) { return await fn.apply(_this, args); } //#endregion export { debounce };