clock.ts (1350B)
1 // Active-time deadline: input/step/idle pauses are excluded. Long delays are 2 // chunked to fit the browser's signed 32-bit timer range. 3 export class ActiveClock { 4 private remaining = Infinity; 5 private since: number | undefined; 6 private timer: ReturnType<typeof setTimeout> | undefined; 7 constructor(private readonly expire: () => void, 8 private readonly now: () => number = () => performance.now(), 9 private readonly schedule: typeof setTimeout = (handler, delay, ...args) => globalThis.setTimeout(handler, delay, ...args), 10 private readonly cancel: typeof clearTimeout = id => globalThis.clearTimeout(id)) {} 11 reset(milliseconds: number): void { 12 if (milliseconds !== Infinity && (!Number.isSafeInteger(milliseconds) || milliseconds < 1)) throw new Error('Invalid active deadline'); 13 this.pause(); this.remaining = milliseconds; 14 } 15 start(): void { 16 if (this.since !== undefined || this.remaining === Infinity) return; 17 this.since = this.now(); 18 this.timer = this.schedule(() => { 19 this.pause(); 20 if (this.remaining <= 0) this.expire(); else this.start(); 21 }, Math.min(2_147_483_647, Math.max(0, this.remaining))); 22 } 23 pause(): void { 24 this.cancel(this.timer); 25 if (this.since !== undefined) this.remaining -= this.now() - this.since; 26 this.since = undefined; this.timer = undefined; 27 } 28 }