Search code examples
angularrxjsswitchmapconcatmap

Rxjs concatMap that resolves to switchMap


Could you please clarify what is going on here and whether this is a legitimate approach?

I have a method for fetching data. When data is fetched, I want to store it in a BehaviorSubject:

test$ = new BehaviorSubject<any[] | undefined>(undefined);

async test2$(): Promise<any[] | undefined>  {
    //...network request...
    this.test$.next([2]);
    return [2];
}

However, I am aware that the cached data may not have been fetched yet, so I must first check for the default value - undefined:

this.test$
    .pipe(
        concatMap((n) =>
            n?.length ? of(n) : from(this.test2$()).pipe(switchMap(() => this.test$))
        )
    )
    .subscribe((n) => {
        console.log('subscribe n', n);
    });

What I'm afraid of is an endless loop, which is exactly what happens without this line pipe(switchMap(() => this.test$)), but I'm not sure how the switchMap helps here.

I believe the pipe eventually resolves to this.test$ again, and all emissions to concatMap, except the first, are cancelled?


Solution

  • Take this approach instead :

    private cache = new BehaviorSubject<any>(undefined);
    
    public cache$ = this.cache.asObservable().pipe(
      filter(Boolean);
    );
    
    loadData() {
      if (this.cache.value) return this.cache$.pipe(first());
      else return this.fetchData();
    }
    
    private fetchData() {
      return this.http.get('...').pipe(
        tap(res => this.cache.next(res))
      );
    }
    

    This will load the data if it's not present, otherwise it will return the cache, and either way, your subscription will not work unless there is data in the cache (thanks to the filter operator)