import { deferred } from 'promise-assist'; /** * Cancelable debouncing of calls to trigger * @example waitTime * ```ts * const debounced = new Debouncer(a => console.log(a), 1, 100) * debounce.trigger('first') * debounce.trigger('second') * // after 1ms * // => 'second' * ``` * @example maxWaitTime * ```ts * const debounced = new Debouncer(a => console.log(a), 3, 4) * debounce.trigger('first') * // after 1ms * debounce.trigger('second') * // after 1ms * // => 'second' * debounce.trigger('third') * // after 2ms, 4ms elapsed since the first trigger * // => 'third' * ``` */ export class Debouncer any> { private timeout: ReturnType | undefined; private maxTimeout: ReturnType | undefined; private args = [] as any as Parameters; private defer = deferred>(); /** * * @param cb - trigger callback * @param waitTime - time to wait before invoking cb * @param maxWaitTime - maximum wait time from first triggering */ constructor( private cb: T, private waitTime: number, private maxWaitTime: number, ) {} private execute() { try { const result = this.cb(...this.args) as ReturnType; this.defer.resolve(result); } catch (ex) { this.defer.reject(ex); } this.defer = deferred(); } /** * * @param args - arguments to pass to cb * @returns Promised resolved with cb return value */ trigger(...args: Parameters) { this.args = args; if (this.timeout) { clearTimeout(this.timeout); } this.timeout = setTimeout(() => { this.execute(); if (this.maxTimeout) { clearTimeout(this.maxTimeout); } }, this.waitTime); if (!this.maxTimeout) { this.maxTimeout = setTimeout(() => { this.execute(); if (this.timeout) { clearTimeout(this.timeout); } }, this.maxWaitTime); } return this.defer.promise; } /** * Cancels pending invocation of cb * @example * ```ts * const debounced = new Debouncer(a => console.log(a), 3, 4) * debounce.trigger('first') * debounce.cancel() * // console.log will not be invoked * ``` */ cancel() { if (this.timeout) { clearTimeout(this.timeout); } if (this.maxTimeout) { clearTimeout(this.maxTimeout); } } setCb(cb: T): void { this.cb = cb; } }