In this post I will talk about error handling in complex RxJs streams.
The easiest way to trap errors in RxJs is to add an error callback in the subscribe call.
While this is super simple, this approach has a few shortcomings. The main issue is that if you let an error propagate up to the “catch all” handler, the underlying observable is terminated in the process.
This presents a problem for long running observables where you want to keep emitting values even after an error has occurred.
Take for example a master detail list. You don't want your UI to stop working if clicking some of the items in the list results in an error. With the "catch all" approach, the observable chain is terminated and stops emitting values.
In my demo app I have created a list of cars that you can click to get more details about specific car models.
Requesting car details is done through a sequence of http requests. I have composed a chained observable to facilitate the requests as seen below.
I start things up with a Subject that emits values based on user clicks.
Whenever the user clicks a link in the UI, an event is dispatched from the Subject.
The values are then chained through a sequence of additional http requests in carService.getModels().
The first step is to make an http request to get details about a specific car. The response from this request is a list of urls that we can fetch in parallel to get specific car models.
As you can tell, I have composed an observable by switchMapping, flatMapping and forkJoining.
This call sequence presents us with two distinct error scenarios:
The first call to get a specific car may fail, but we may also have failures in any of the parallel car model calls. I have both cases occurring in my sample application.
As I mentioned earlier I could have implemented error handling as a simple error callback in the subscribe. However, the UI would stop working after the first error. Not a great experience!
Instead I have found that the best way to deal with errors in the observable chain is to trap the errors “in place” using the catch operator. This allows me to transform the error to a valid response that ensures that the observable will stay alive, even after errors.
One of the things I like about RxJs is that “an Observable is an Observable”, regardless of the operator that produced it. Here I am using catch to return an observable that is compatible with the response of a successful call.
I deal with errors in the first call by returning an “empty” observable. This is similar to a request without detail records. To distinguish an error from a valid empty result set I pass along an error flag.
For model requests I add a catch to each request in the forkJoin. This allows for partial failures in the detail requests. Basically, we don't want to fail all model requests just because a single model has an issue.
In my case I am transforming failing models to a known error model object that I can handle in the view.
I have deployed a simple demo of this here.
In the UI I am handling both types of error scenarios.
The code is available on Github>.