Billy
Billy

Reputation: 409

Using ShareReplay() and to cache result of observable - how to refresh and use new value in another observable

I am using shareReplay() to cache an http call so it isn't repeated. However, at some point, I need to refresh the API call. Following various suggestions in stackoverflow, I introduce a Subject, so something like this:

  private _refreshResponse$ = new BehaviorSubject<void>(undefined);

  public response$: Observable<any> = this._refreshResponse$.pipe(
    switchMap(() => this.http.get<any>(this.apiUrl)),
    map((o) => o.message),
    shareReplay(1)
  );

  constructor(private http: HttpClient) {
    this.response$.subscribe();
  }

  public onRefreshClick() {
    this.refreshResponse();
  }

  public refreshResponse() {
    this._refreshResponse$.next();
  }

And calling onRefreshClick() does indeed call the API again.

I introduced a new method to do some more work. I want to make an http call first, which will then call refreshResponse(), and then some work with the new value. Something like the following:

  public onDoSomeWork() {
    // just create an observable... imagine it is an http call or something
    this.fakeHttpCall()
      .pipe(
        tap(() => this.refreshResponse()),
        switchMap(() =>
          this.response$.pipe(
            take(1),
            tap((o) => console.log(o))
          )
        )
      )
      .subscribe();
  }

But, of course, refreshResponse() does not wait for the returned value, so the value I get from response$ immediately after it is the previous value, NOT the new value. How can I "wait" for the new value.

Here's a demo: https://stackblitz.com/edit/angular-y2dtlg . The "response" is displayed in the browser (just a url from some random API I found). If you open the console, and click the "Work" button, it will refresh the URL displayed, and also write a URL to the console.log. But the console.log value is the previous value, not the updated one.

Note: I don't want to do the "console.log" in the this.response$.subscribe(() => ...) method.

Thanks in advance!

EDIT: One (awful!) solution is to add a delay:

  public onDoSomeWork() {
    // just create an observable... imagine it is an http call or something
    this.fakeHttpCall()
      .pipe(
        tap(() => this.refreshResponse()),
        delay(2000),
        switchMap(() =>
          this.response$.pipe(
            take(1),
            tap((o) => console.log(o))
          )
        )
      )
      .subscribe();
  }

This is just to show what I expect/where the problem lies if it's not clear...

Upvotes: 1

Views: 54

Answers (0)

Related Questions