import { createDeferredPromise, DeferredPromise, } from '../util/DeferredPromise.js'; import {assert} from '../util/assert.js'; /** * @internal */ export interface Poller { start(): Promise; stop(): Promise; result(): Promise; } /** * @internal */ export class MutationPoller implements Poller { #fn: () => Promise; #root: Node; #observer?: MutationObserver; #promise?: DeferredPromise; constructor(fn: () => Promise, root: Node) { this.#fn = fn; this.#root = root; } async start(): Promise { const promise = (this.#promise = createDeferredPromise()); const result = await this.#fn(); if (result) { promise.resolve(result); return; } this.#observer = new MutationObserver(async () => { const result = await this.#fn(); if (!result) { return; } promise.resolve(result); await this.stop(); }); this.#observer.observe(this.#root, { childList: true, subtree: true, attributes: true, }); } async stop(): Promise { assert(this.#promise, 'Polling never started.'); if (!this.#promise.finished()) { this.#promise.reject(new Error('Polling stopped')); } if (this.#observer) { this.#observer.disconnect(); this.#observer = undefined; } } result(): Promise { assert(this.#promise, 'Polling never started.'); return this.#promise; } } export class RAFPoller implements Poller { #fn: () => Promise; #promise?: DeferredPromise; constructor(fn: () => Promise) { this.#fn = fn; } async start(): Promise { const promise = (this.#promise = createDeferredPromise()); const result = await this.#fn(); if (result) { promise.resolve(result); return; } const poll = async () => { if (promise.finished()) { return; } const result = await this.#fn(); if (!result) { window.requestAnimationFrame(poll); return; } promise.resolve(result); await this.stop(); }; window.requestAnimationFrame(poll); } async stop(): Promise { assert(this.#promise, 'Polling never started.'); if (!this.#promise.finished()) { this.#promise.reject(new Error('Polling stopped')); } } result(): Promise { assert(this.#promise, 'Polling never started.'); return this.#promise; } } export class IntervalPoller implements Poller { #fn: () => Promise; #ms: number; #interval?: NodeJS.Timer; #promise?: DeferredPromise; constructor(fn: () => Promise, ms: number) { this.#fn = fn; this.#ms = ms; } async start(): Promise { const promise = (this.#promise = createDeferredPromise()); const result = await this.#fn(); if (result) { promise.resolve(result); return; } this.#interval = setInterval(async () => { const result = await this.#fn(); if (!result) { return; } promise.resolve(result); await this.stop(); }, this.#ms); } async stop(): Promise { assert(this.#promise, 'Polling never started.'); if (!this.#promise.finished()) { this.#promise.reject(new Error('Polling stopped')); } if (this.#interval) { clearInterval(this.#interval); this.#interval = undefined; } } result(): Promise { assert(this.#promise, 'Polling never started.'); return this.#promise; } }