1

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...

4
  • 1
    would skip(1) fit your needs? Commented Nov 15, 2024 at 14:42
  • @Andrei no I don't think so. OnDoSomeWork() is linked to a button and clicked multiple times. I just kind of want a work flow where I 1) make "fake" http call 2) refresh the sharereplay observable (another http call) 3) wait for the refresh to complete to then continue executing the next thing in the pipe Commented Nov 15, 2024 at 14:50
  • i would make the response as this: response$ = ...switchMap(() => merge(of(null), makeRequest())), shareReplay(1), filter(Boolean). this way nulls will be sareReplayed, but filtered out for subscribers. i.e. for subscribers before refresh - they would receive old value, for subs after refresh they would wait for the new value Commented Nov 15, 2024 at 15:07
  • That appears to work, thank you! Commented Nov 15, 2024 at 16:20

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.