chromium/ash/webui/camera_app_ui/resources/js/waitable_event.ts

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * A waitable event for synchronization between asynchronous jobs.
 */
export class WaitableEvent<T = void> {
  private isSignaledInternal = false;

  // The field is definitely assigned in the constructor since the argument to
  // the Promise constructor is called immediately, but TypeScript can't
  // recognize that. Disable the check by adding "!" to the property name.
  protected resolve!: (val: PromiseLike<T>|T) => void;

  protected reject!: (val: Error) => void;

  private readonly promise: Promise<T>;

  constructor() {
    this.promise = new Promise((resolve, reject) => {
      this.resolve = resolve;
      this.reject = reject;
    });
  }

  isSignaled(): boolean {
    return this.isSignaledInternal;
  }

  /**
   * Signals the event.
   */
  signal(value: T): void {
    if (this.isSignaledInternal) {
      return;
    }
    this.isSignaledInternal = true;
    this.resolve(value);
  }

  /**
   * @return Resolved when the event is signaled.
   */
  wait(): Promise<T> {
    return this.promise;
  }

  /**
   * @param timeout Timeout in ms.
   * @return Resolved when the event is signaled, or rejected when timed out.
   */
  timedWait(timeout: number): Promise<T> {
    const timeoutPromise = new Promise<T>((_resolve, reject) => {
      setTimeout(() => {
        reject(new Error(`Timed out after ${timeout}ms`));
      }, timeout);
    });
    return Promise.race([this.promise, timeoutPromise]);
  }
}

export class CancelableEvent<T> extends WaitableEvent<T> {
  signalError(e: Error): void {
    this.reject(e);
  }

  signalAs(promise: Promise<T>): void {
    this.resolve(promise);
  }
}