RxJS Mastery – #61 catchError

RxJS catchError

The RxJS catchError operator is applied when an error is thrown before it. As arguments, the operator receives the thrown error and the source Observable. Importantly, catchError itself returns an Observable.

RxJS allows to react to errors

In RxJS marbles an error is indicated by #. The catchError operator enables us to react to errors similarly as we would use other operators. There is no need for a try-catch, but we can do the error handling in the RxJS way. In the below example, we are catching the error and returning a proper Observable from it to complete the stream nicely for the subscribers.

const source$ = cold('abc#def').pipe(
    catchError(_ => of('Error thrown')),
);

expectObservable(source$).toBe(
    'abc(z|)',
    { a: 'a', b: 'b', c: 'c', z: 'Error thrown' });

RxJS catchError returns a replacement Observable

Once an Observable errors out it cannot be used anymore. No more values will be delivered by it. However, this does not prevent us from resubscribing to a replacement Observable. In the example below, we call a service that returns an array of numbers.

const source$ = cold('#r', { r: [1, 2, 3]}).pipe(
    catchError(_ => of([])),
);

expectObservable(source$).toBe(
    '(d|)',
    { d: [] }
);

If something goes wrong along this chain we can use catchError to return a replacement Observable. In the example, some error happens before the response r is even returned. The default value [] is instead emitted. Of course, one has to be careful not to hide errors completely and have unwanted behavior in the application as a consequence.

RxJS catchError with catch and rethrow

The catchError subscribes to a new Observable and that new Observable of course can also throw an error. This fact is often used to handle errors in a specific way. In the below example, we are checking if the error of the Observable was an HTTP 404 error. If so, we are emitting a different error message “Resource not found”.

const source$ = cold('-#', undefined, new Error('HTTP 404')).pipe(
    catchError((err: Error, caught) => {
        console.log('Error happened: ' + err.message);
        if (err.message === 'HTTP 404') {
            throw new Error('Resource not found');
        }
        throw new Error('General error');
    }),
);

expectObservable(source$).toBe(
    '-#', undefined, new Error('Resource not found')
);

Exercise for the RxJS catchError operator 💪

Write an Observer that handles the error in the below example and logs to the console with console.error.

const source$ = cold('-#', undefined, new Error('HTTP 404')).pipe(
    catchError((err: Error, caught) => {
        console.log('Error happened: ' + err.message);
        if (err.message === 'HTTP 404') {
            throw new Error('Resource not found');
        }
        throw new Error('General error');
    }),
);

This post is part of the RxJS mastery series. As always the code examples can be found on GitHub.