BACK
Lessons35. catchError inside switchMap
Lessons · 35

35. catchError inside switchMap

Where you put catchError is critical

From the previous lesson you know an error travels up the pipe until it meets a catchError. But there's a subtlety that's very important to grasp for real apps:

Once an error is caught, the stream completes at that point. An outer source below the catch keeps living. An outer source above the catch is dead too.

Two options

// BAD: catchError on the outside — kills the search field
query$.pipe(
  switchMap(q => fakeRequest(q)),
  catchError(...)   // after the first error, query$ is dead
)

// GOOD: catchError inside switchMap — only one request fails
query$.pipe(
  switchMap(q => fakeRequest(q).pipe(
    catchError(...)  // caught here — the outer query$ stays alive
  ))
)

A real scenario

A live-search field: the user types an odd query and the server returns an error. With catchError on the outside, the next keystroke won't work — the stream is dead. With it inside, only one request failed, and the next keystroke fires switchMap again.

Your task

  1. Inside the function passed to switchMap, in fakeRequest(query).pipe(...), add catchError(error => of('Fallback: ' + query)).
  2. Notice: catchError sits in the inner pipe, not the outer one.
  3. Expected output: Result: ok → Fallback: bad → Result: next. After bad fails, the stream is alive and handles next.
Solution spoiler · click to reveal
const { from, of, throwError, switchMap, catchError } = Rx;

function fakeRequest(query) {
  if (query === 'bad') {
    return throwError(() => 'Request failed');
  }
  return of('Result: ' + query);
}

const query$ = from(['ok', 'bad', 'next']);

const result$ = query$.pipe(
  switchMap(query => {
    return fakeRequest(query).pipe(
      catchError(error => of('Fallback: ' + query))
    );
  })
);

result$.subscribe(value => console.log(value));
script.ts // TypeScript
CONSOLE · Console output
Hit Run to see the result...