UNPKG

rc-js-util

Version:

A collection of TS and C++ utilities to help writing performant and correct applications, achieved through strict typing and (removable) invariant checking.

114 lines (100 loc) 2.72 kB
import { _Debug } from "../debug/_debug.js"; /** * @public * A provider that should return an iterator that represents a sequence of update operations. Useful for breaking up * operations that would block the UI for an unacceptably long time. */ export interface IIncrementallyUpdatable { incrementallyUpdate(): IterableIterator<void>; } /** * @public * {@inheritDoc IncrementalUpdater} */ export interface IIncrementalUpdater { readonly isUpdating: boolean; beginUpdate(): void; cancel(): void; suspend(): void; resume(): void; } /** * @public * Performs update operations once every `waitPeriod` until the iterator returned by {@link IIncrementallyUpdatable} is * exhausted. */ export class IncrementalUpdater implements IIncrementalUpdater { /** * Remains true while the update is suspended. */ public isUpdating: boolean = false; public constructor ( private updatable: IIncrementallyUpdatable, private waitPeriod: number = 4, ) { } /** * Cancel the update and clear the task. */ public cancel(): void { if (this.id != null) { clearTimeout(this.id); this.id = null; this.isUpdating = false; } this.currentIterator = null; } /** * Suspends the current task if one is active. */ public suspend(): void { if (this.id != null) { clearTimeout(this.id); this.id = null; } } /** * Start a new update cycle. If an update was already in progress it will be cancelled. */ public beginUpdate(): void { this.cancel(); this.isUpdating = true; this.update(); } /** * Resumes the currently suspended task. It is an error to call this if there is not a currently suspended task. */ public resume(): void { _BUILD.DEBUG && _Debug.assert(this.isUpdating && this.id == null, "nothing to resume"); this.update(); } private update = (): void => { this.currentIterator ??= this.updatable.incrementallyUpdate(); if (this.currentIterator.next().done === true) { this.currentIterator = null; this.id = null; this.isUpdating = false; } else { this.id = setTimeout(this.update, this.waitPeriod); } }; private currentIterator: IterableIterator<unknown> | null = null; private id: number | null = null; } declare function clearTimeout(handle?: number): void; declare function setTimeout(handler: () => void, timeout?: number, ...arguments: unknown[]): number;