Skip to content

Instantly share code, notes, and snippets.

@pjlsergeant
Last active March 7, 2024 03:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pjlsergeant/4afc3c78a8abd1e4e0862b414f5fdb77 to your computer and use it in GitHub Desktop.
Save pjlsergeant/4afc3c78a8abd1e4e0862b414f5fdb77 to your computer and use it in GitHub Desktop.
/*
# 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 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) ) );
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;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment