/** * @file throttle.ts * @description Leading + trailing throttle for live-refresh callbacks. Unlike a * trailing debounce, a steady stream of WebSocket events can never postpone the * refresh forever: it fires at once, then at most once per `ms`, and once more * after the burst so the last change is never lost. * @author Nguyễn Ngọc Trí Vĩ */ export interface Throttled { (): void; cancel(): void; } export function throttle(fn: () => void, ms: number): Throttled { let last = 0; let timer: ReturnType | undefined; const run = () => { timer = undefined; last = Date.now(); fn(); }; const t = (() => { if (timer) return; const wait = last + ms - Date.now(); if (wait <= 0) run(); else timer = setTimeout(run, wait); }) as Throttled; t.cancel = () => { clearTimeout(timer); timer = undefined; }; return t; }