javascriptrxjs

resolve promise multiple times


I'm completely new to rxjs and trying to accomplish one task - essentially I need a Promise-like API with the following differences:

  1. Unlike the standard Promise my API should be able to resolve multiple times.
  2. When calling await/then the last resolved value should be used.
  3. If my API was never resolved the await/then should hung up, just like the standard Promise.
  4. Additionally I need a reset method which will be resetting my API object so the following await/then calls will hung up like in the point 3.

How can I implement the needed behaviour? I think rxjs Observable and lastValueFrom is what I need, but I just can't figure out how to use it.

Pseudocode example usage:

const myAuthObservable = new MyObservableApi<Auth>((resolve) =>
  subscribe(
    (s) => s.auth,
    (auth) => {
      if (auth) {
        resolve(auth); // this can be called multiple times
      }
    }
  )
);

emitter.on("start", async () => {
  // always the last auth will be returned
  // or the api will hung up until the first auth is emitted
  const auth = await myAuthObservable;
  myAuthObservable.reset();
});

Solution

  • after reading @Barmar comment I eventually came up with a simple non-rxjs solution

    It sounds like what you want is an object that caches the value that the promise resolves to, then returns that cached value on future uses

    export class MultiResolve<T> {
      private readonly initialSymbol: symbol = Symbol();
      private currentValue: T | symbol = this.initialSymbol;
      private readonly resolvers: Array<(value: T) => void> = [];
    
      resolve(value: T): void {
        this.currentValue = value;
        for (const resolver of this.resolvers) {
          resolver(value);
        }
      }
    
      get promise(): Promise<T> {
        return new Promise(resolve => {
          if (this.currentValue !== this.initialSymbol) {
            resolve(this.currentValue as T);
          }
          this.resolvers.push(resolve);
        });
      }
    
      reset(): void {
        this.currentValue = this.initialSymbol;
      }
    }
    

    usage:

    const multiResolve = new MultiResolve<number>();
    
    async function test() {
      console.log(await multiResolve.promise); // Will wait until the first resolve
    
      multiResolve.resolve(1);
      console.log(await multiResolve.promise); // Will immediately get 1
    
      multiResolve.resolve(undefined as any); // TypeScript requires an explicit cast to 'any' for undefined
      console.log(await multiResolve.promise); // Will immediately get undefined
    
      multiResolve.resolve(2);
      console.log(await multiResolve.promise); // Will immediately get 2
    }
    
    test();
    
    setTimeout(() => {
      multiResolve.resolve(3); // Will immediately get 3 in the next await
    }, 1000);