All files / utilities debounce.ts

100% Statements 22/22
100% Branches 4/4
100% Functions 10/10
100% Lines 22/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87                      268x             46x   46x 22x 22x               48x 24x 24x         1x                 192x         192x             18x   18x 6x 6x               18x   18x 4x 4x         4x 3x     4x      
/**
 * A trailing-edge debouncer: {@link schedule} (re)arms a timer and the callback
 * runs once the calls stop for the configured delay. Replaces the hand-rolled
 * clear/set/dispose timer bookkeeping that otherwise accumulates per call site.
 */
export class Debouncer {
	private _timer: ReturnType<typeof setTimeout> | undefined;
 
	/**
	 * @param _delayMs The trailing delay in milliseconds.
	 */
	constructor(private readonly _delayMs: number) { }
 
	/**
	 * Schedules the callback, resetting any pending timer. Only the callback of
	 * the most recent call runs.
	 */
	schedule(callback: () => void): void {
		this.cancel();
 
		this._timer = setTimeout(() => {
			this._timer = undefined;
			callback();
		}, this._delayMs);
	}
 
	/**
	 * Cancels the pending callback, if any.
	 */
	cancel(): void {
		if (this._timer) {
			clearTimeout(this._timer);
			this._timer = undefined;
		}
	}
 
	dispose(): void {
		this.cancel();
	}
}
 
/**
 * A trailing-edge debouncer with one independent timer per key (e.g. per
 * document URI), so bursts on one key do not delay another.
 */
export class KeyedDebouncer<K = string> {
	private readonly _timers = new Map<K, ReturnType<typeof setTimeout>>();
 
	/**
	 * @param _delayMs The trailing delay in milliseconds.
	 */
	constructor(private readonly _delayMs: number) { }
 
	/**
	 * Schedules the callback for a key, resetting that key's pending timer. Only
	 * the callback of the most recent call per key runs.
	 */
	schedule(key: K, callback: () => void): void {
		this.cancel(key);
 
		this._timers.set(key, setTimeout(() => {
			this._timers.delete(key);
			callback();
		}, this._delayMs));
	}
 
	/**
	 * Cancels the pending callback for a key, if any.
	 */
	cancel(key: K): void {
		const timer = this._timers.get(key);
 
		if (timer) {
			clearTimeout(timer);
			this._timers.delete(key);
		}
	}
 
	dispose(): void {
		for (const timer of this._timers.values()) {
			clearTimeout(timer);
		}
 
		this._timers.clear();
	}
}