Semen Shekhovtsov
Semen Shekhovtsov

Reputation: 882

Wait in the function that returns observable

There is function, which loads user details. This function returns observable. It is called from multiple places. To avoid redundant calls I'd like to wait somehow after the first call until the result is loaded. Any idea haw this can be done using RxJs and Angular?

    getUser(): Observable<any> {
      return this.http.get('/userAuthenticated');
    }

The idea is to wait somehow until the first call is completed and save result in the static variable:

    getUser(): Observable<any> {
      if(!this.userIsLoading) {
        this.userIsLoading = true;
        return this.http.get('/userAuthenticated').pipe(
          tap((res: any) => {
            this.userIsLoading = false;
            this.User = res.data;
        }));
      } else {
        // wait until the first call is completed
        return of(this.User);
      }
    }

another idea is to define a static variable for the observable.

please help

Upvotes: 0

Views: 1039

Answers (3)

enno.void
enno.void

Reputation: 6579

req$ = this.http.get('/userAuthenticated').pipe(shareReplay(1));

getUser(): Observable<any> {
  return this.req$
}

You can use the shareReplay operator to cache the result after the first (and then only) call to the backend. Any late subscriber will get the cached result

You can read more about shareReplay here: https://rxjs.dev/api/operators/shareReplay

Upvotes: 1

Amer
Amer

Reputation: 6716

To achieve that, you can:

  • Create a new Observable property under your service/component, with the shareReplay operator to avoid calling the API many times and share the result with each one of the new subscribers.
  • Return the new Observable from the getUser function (You can't add the shareReplay directly here, because every time the getUser is called, it returns a new Observable with shareReplay, without returning the same one, which won't work in your case).

Try something like the following:

// import { shareReplay } from 'rxjs/operators';

private userSource$ = this.http.get('/userAuthenticated').pipe(shareReplay());

getUser(): Observable<any> {
  return this.userSource$;
}

Upvotes: 1

Chris Hamilton
Chris Hamilton

Reputation: 10994

Rxjs has the function firstValueFrom() which returns a promise that you can await.

  async getUser(): Promise<Observable<any>> {
    if (!this.userIsLoading) {
      this.userIsLoading = true;
      return this.http.get('/userAuthenticated').pipe(
        tap((res: any) => {
          this.userIsLoading = false;
          this.User = res.data;
        })
      );
    } else {
      await firstValueFrom(this.http.get('/userAuthenticated'));
      return of(this.User);
    }
  }

Upvotes: 0

Related Questions