Search code examples
javascriptrxjsobservablecombinelatest

combineLatest. continue observables after getting error


I have couple of observables combined in one observable with combineLatest. Also I have one inner switchMap observable that in real example makes an http request to remote server.

Now I wonder, how can the combined observable continue working in case if switchMap returns error ?

I've created simplified example here

//emit every 2.5 seconds
const first = interval(2500);
//emit every 2 seconds
const second = interval(2000);
//emit every 1.5 seconds
const third = interval(1500);
//emit every 1 second
const fourth = interval(1000);

let count = 0;

//emit outputs from one observable
const example = combineLatest(
  first.pipe(mapTo("FIRST!")),
  second.pipe(mapTo("SECOND!")),
  third.pipe(mapTo("THIRD")),
  fourth.pipe(mapTo("FOURTH"))
)
  .pipe(
    switchMap(data => {
      console.log(data);
      count++;
      // here lets asume in some cases http request getting error
      return count < 5 ? of("switchMap") : throwError("This is an error!");
    }),
    catchError(err => of(err))
  )
  .subscribe(val => console.log(val));

Output

["FIRST!", "SECOND!", "THIRD", "FOURTH"]
switchMap
["FIRST!", "SECOND!", "THIRD", "FOURTH"]
switchMap
["FIRST!", "SECOND!", "THIRD", "FOURTH"]
switchMap
["FIRST!", "SECOND!", "THIRD", "FOURTH"]
switchMap
["FIRST!", "SECOND!", "THIRD", "FOURTH"]
This is an error!

So after getting error combineLatest observable's work stops. In my real example I have 4 filters, and after changing filters I make http request.


Solution

  • The stream itself from the combineLatest will end when an error occurs.
    You can prevent it by adding the catchError to the Observable returned in your switchMap.

    That way, the main stream is not altered and will continue to live.

    const first  = interval(2500);
    const second = interval(2000);
    const third  = interval(1500);
    const fourth = interval(1000);
    
    let count = 0;
    
    combineLatest(
      first.pipe(mapTo("FIRST!")),
      second.pipe(mapTo("SECOND!")),
      third.pipe(mapTo("THIRD")),
      fourth.pipe(mapTo("FOURTH"))
    ).pipe(
      switchMap(data => {
        count++;
        const obs$ = count < 5
          ? of("switchMap")
          : throwError("This is an error!");
    
        return obs$.pipe(
          catchError(err => of(err))
        );
      })
    ).subscribe(val => console.log(val));