Skip to content

Instantly share code, notes, and snippets.

@pjlsergeant
Last active March 7, 2024 03:31
Show Gist options
  • Select an option

  • Save pjlsergeant/4afc3c78a8abd1e4e0862b414f5fdb77 to your computer and use it in GitHub Desktop.

Select an option

Save pjlsergeant/4afc3c78a8abd1e4e0862b414f5fdb77 to your computer and use it in GitHub Desktop.

Revisions

  1. pjlsergeant revised this gist Mar 7, 2024. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions AwaitOnce.ts
    Original file line number Diff line number Diff line change
    @@ -10,8 +10,8 @@ before it's ready.
    const once = new AwaitOnce<string>();
    // Only the first call to run() is executed, the other calls just get back
    // the same promise the first call had.
    // Only the first call to run() is executed, the other calls just wait
    // until that's resolved, and return the initially returned result
    const p1 = once.run( () => delay('slow', 1000) ) );
    const p2 = once.run( () => delay('fast', 100) ) );
  2. pjlsergeant created this gist Mar 4, 2024.
    46 changes: 46 additions & 0 deletions AwaitOnce.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,46 @@
    /*
    # Why
    Asynchronously instantiate a singleton only once, even if many calls are made,
    eg, a handler that lazy-loads a resource, but might be called several times
    before it's ready.
    # Synopsis
    const once = new AwaitOnce<string>();
    // Only the first call to run() is executed, the other calls just get back
    // the same promise the first call had.
    const p1 = once.run( () => delay('slow', 1000) ) );
    const p2 = once.run( () => delay('fast', 100) ) );
    const p3 = once.run( () => delay('faster', 10) ) );
    const results = await Promise.all( promises );
    expect( results ).toEqual(['slow', 'slow', 'slow']);
    */

    export class AwaitOnce<T> {
    #promise: Promise<T>;
    #running: boolean = false;
    #resolve!: (value: T | PromiseLike<T>) => void;
    #reject!: (reason?: any) => void;

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

    run( fn: () => Promise<T> ) {
    if ( ! this.#running ) {
    this.#running = true;
    fn().then( this.#resolve ).catch( this.#reject );
    }

    return this.#promise;
    }
    }