/** * A disposable object */ export interface IDisposable { /** 释放进程 */ dispose(): void } /** * Execute the callback the next time the browser is idle, returning an * {@link IDisposable} that will cancel the callback when disposed. This wraps * [requestIdleCallback] so it will fallback to [setTimeout] if the environment * doesn't support it. * * @param callback The callback to run when idle, this includes an * [IdleDeadline] that provides the time alloted for the idle callback by the * browser. Not respecting this deadline will result in a degraded user * experience. * @param timeout A timeout at which point to queue no longer wait for an idle * callback but queue it on the regular event loop (like setTimeout). Typically * this should not be used. * * [IdleDeadline]: https://developer.mozilla.org/en-US/docs/Web/API/IdleDeadline * [requestIdleCallback]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback * [setTimeout]: https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout */ export let runWhenIdle: (callback: (idle: IdleDeadline) => void, timeout?: number) => IDisposable declare function requestIdleCallback( callback: (args: IdleDeadline) => void, options?: { timeout: number }, ): number declare function cancelIdleCallback(handle: number): void if (typeof requestIdleCallback !== 'function' || typeof cancelIdleCallback !== 'function') { runWhenIdle = (runner) => { setTimeout(() => { if (disposed) { return } const end = Date.now() + 15 // one frame at 64fps runner( Object.freeze({ didTimeout: true, timeRemaining() { return Math.max(0, end - Date.now()) }, }), ) }) let disposed = false return { dispose() { if (disposed) { return } disposed = true }, } } } else { runWhenIdle = (runner, timeout?) => { const handle: number = requestIdleCallback( runner, typeof timeout === 'number' ? { timeout } : undefined, ) let disposed = false return { dispose() { if (disposed) { return } disposed = true cancelIdleCallback(handle) }, } } } /** * An implementation of the "idle-until-urgent"-strategy as introduced * 将计算过程推迟到浏览器空闲时运行,这可以避免在浏览器不空闲时执行计算操作,从而提高页面性能。 * here: https://philipwalton.com/articles/idle-until-urgent/ */ export class IdleValue { private readonly _executor: () => void private readonly _handle: IDisposable private _didRun = false private _value?: T private _error: unknown constructor(executor: () => T) { this._executor = () => { try { this._value = executor() } catch (err) { this._error = err } finally { this._didRun = true } } this._handle = runWhenIdle(() => this._executor()) } /** 用于释放该对象 */ dispose(): void { this._handle.dispose() } /** 用于获取计算后的值 */ get value(): T { if (!this._didRun) { this._handle.dispose() this._executor() } if (this._error) { throw this._error } return this._value! } /** 用于检查该对象的值是否已经初始化过 */ get isInitialized(): boolean { return this._didRun } } //#endregion