Skip to content

Instantly share code, notes, and snippets.

@SamJakob
Created February 19, 2024 21:16

Revisions

  1. SamJakob created this gist Feb 19, 2024.
    86 changes: 86 additions & 0 deletions completer.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,86 @@
    /**
    * This is a TypeScript version of the `Completer` class from Dart.
    *
    * A `Completer` provides a future that can be imperatively concluded (once and
    * only once) with either `#complete` or `#completeError`.
    * Once the completer has completed (or errored), the future will be resolved
    * with the specified value.
    *
    * This is trivially implemented by instantiating a Promise and capturing the
    * resolve and reject functions.
    */
    export class Completer<T> {
    readonly #promise: Promise<T>;
    #resolve: (value: (T | PromiseLike<T>)) => void;
    #reject: (reason?: any) => void;

    #completed = false;

    /**
    * Initialize a {@link Completer}.
    *
    * A Completer provides a future that can be imperatively concluded (once)
    * with either {@link #complete} or {@link #completeError}. Once the
    * completer has completed (or errored), the future will be resolved with
    * the specified value. It will then not be possible to complete the
    * completer again (as it will throw an error if an attempt is made to do
    * so).
    */
    public constructor() {
    // Initialize #resolve and #reject with a function that throws an error
    // so errors are not silently swallowed if the completer is not
    // initialized properly.
    const failedInitialize = () => {
    throw new Error("Completer failed to initialize");
    };

    this.#resolve = failedInitialize;
    this.#reject = failedInitialize;

    // Initialize the #promise property with a new promise and define the
    // #resolve and #reject functions to be the resolve and reject functions
    // of the promise.
    this.#promise = new Promise((resolve, reject) => {
    this.#resolve = resolve;
    this.#reject = reject;
    });
    }

    /**
    * Returns the promise that can be used to observe the completion of the
    * completer.
    */
    public get promise(): Promise<T> { return this.#promise; }

    /**
    * Returns true if either {@link #complete} or {@link #completeError} has
    * been called on the completer.
    */
    public get completed(): boolean { return this.#completed; }

    /**
    * Complete the completer with the specified value.
    * @param value The value to complete the completer with.
    */
    public complete(value: T) {
    if (this.#completed) {
    throw new Error("Completer has already completed");
    }

    this.#completed = true;
    this.#resolve(value);
    }

    /**
    * Complete the completer with the specified error.
    * @param error The error to complete the completer with.
    */
    public completeError(error: any) {
    if (this.#completed) {
    throw new Error("Completer has already completed");
    }

    this.#completed = true;
    this.#reject(error);
    }
    }