tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Poller.ts (3672B)


      1 /**
      2 * @license
      3 * Copyright 2022 Google Inc.
      4 * SPDX-License-Identifier: Apache-2.0
      5 */
      6 
      7 import {assert} from '../util/assert.js';
      8 import {Deferred} from '../util/Deferred.js';
      9 
     10 /**
     11 * @internal
     12 */
     13 export interface Poller<T> {
     14  start(): Promise<void>;
     15  stop(): Promise<void>;
     16  result(): Promise<T>;
     17 }
     18 
     19 /**
     20 * @internal
     21 */
     22 export class MutationPoller<T> implements Poller<T> {
     23  #fn: () => Promise<T>;
     24 
     25  #root: Node;
     26 
     27  #observer?: MutationObserver;
     28  #deferred?: Deferred<T>;
     29  constructor(fn: () => Promise<T>, root: Node) {
     30    this.#fn = fn;
     31    this.#root = root;
     32  }
     33 
     34  async start(): Promise<void> {
     35    const deferred = (this.#deferred = Deferred.create<T>());
     36    const result = await this.#fn();
     37    if (result) {
     38      deferred.resolve(result);
     39      return;
     40    }
     41 
     42    this.#observer = new MutationObserver(async () => {
     43      const result = await this.#fn();
     44      if (!result) {
     45        return;
     46      }
     47      deferred.resolve(result);
     48      await this.stop();
     49    });
     50    this.#observer.observe(this.#root, {
     51      childList: true,
     52      subtree: true,
     53      attributes: true,
     54    });
     55  }
     56 
     57  async stop(): Promise<void> {
     58    assert(this.#deferred, 'Polling never started.');
     59    if (!this.#deferred.finished()) {
     60      this.#deferred.reject(new Error('Polling stopped'));
     61    }
     62    if (this.#observer) {
     63      this.#observer.disconnect();
     64      this.#observer = undefined;
     65    }
     66  }
     67 
     68  result(): Promise<T> {
     69    assert(this.#deferred, 'Polling never started.');
     70    return this.#deferred.valueOrThrow();
     71  }
     72 }
     73 
     74 /**
     75 * @internal
     76 */
     77 export class RAFPoller<T> implements Poller<T> {
     78  #fn: () => Promise<T>;
     79  #deferred?: Deferred<T>;
     80  constructor(fn: () => Promise<T>) {
     81    this.#fn = fn;
     82  }
     83 
     84  async start(): Promise<void> {
     85    const deferred = (this.#deferred = Deferred.create<T>());
     86    const result = await this.#fn();
     87    if (result) {
     88      deferred.resolve(result);
     89      return;
     90    }
     91 
     92    const poll = async () => {
     93      if (deferred.finished()) {
     94        return;
     95      }
     96      const result = await this.#fn();
     97      if (!result) {
     98        window.requestAnimationFrame(poll);
     99        return;
    100      }
    101      deferred.resolve(result);
    102      await this.stop();
    103    };
    104    window.requestAnimationFrame(poll);
    105  }
    106 
    107  async stop(): Promise<void> {
    108    assert(this.#deferred, 'Polling never started.');
    109    if (!this.#deferred.finished()) {
    110      this.#deferred.reject(new Error('Polling stopped'));
    111    }
    112  }
    113 
    114  result(): Promise<T> {
    115    assert(this.#deferred, 'Polling never started.');
    116    return this.#deferred.valueOrThrow();
    117  }
    118 }
    119 
    120 /**
    121 * @internal
    122 */
    123 
    124 export class IntervalPoller<T> implements Poller<T> {
    125  #fn: () => Promise<T>;
    126  #ms: number;
    127 
    128  #interval?: NodeJS.Timeout;
    129  #deferred?: Deferred<T>;
    130  constructor(fn: () => Promise<T>, ms: number) {
    131    this.#fn = fn;
    132    this.#ms = ms;
    133  }
    134 
    135  async start(): Promise<void> {
    136    const deferred = (this.#deferred = Deferred.create<T>());
    137    const result = await this.#fn();
    138    if (result) {
    139      deferred.resolve(result);
    140      return;
    141    }
    142 
    143    this.#interval = setInterval(async () => {
    144      const result = await this.#fn();
    145      if (!result) {
    146        return;
    147      }
    148      deferred.resolve(result);
    149      await this.stop();
    150    }, this.#ms);
    151  }
    152 
    153  async stop(): Promise<void> {
    154    assert(this.#deferred, 'Polling never started.');
    155    if (!this.#deferred.finished()) {
    156      this.#deferred.reject(new Error('Polling stopped'));
    157    }
    158    if (this.#interval) {
    159      clearInterval(this.#interval);
    160      this.#interval = undefined;
    161    }
    162  }
    163 
    164  result(): Promise<T> {
    165    assert(this.#deferred, 'Polling never started.');
    166    return this.#deferred.valueOrThrow();
    167  }
    168 }