Angular CatchError is an RxJs Operator. We can use it to handle the errors thrown by the Angular Observable. Like all other RxJs operators, the CatchError
also takes an observable as input and returns an observable (or throws an error). We can use CatchError to provide a replacement observable or throw a user-defined error. Let us learn all these in this tutorial.
Catch operator was renamed as catchError in RxJs 5.5, Hence if you are using Angular 5 or prior version then use catch instead of catchError.
Table of Contents
Handling Errors in Observable
We can handle the errors at two places.
- Using the
error
callback of thesubscribe
method - Catch errors in the observable stream
Using Error Callback of Subscribe method
We subscribe to an Observable by using the subscribe
method. The subscribe method accepts three callback methods as arguments. They are the next
value, error
, or complete
event. We use the error callback to catch & handle the errors.
For Example, consider the following code. The obs
observable multiplies the values (srcArray
) by 2 using the map operator. If the result is NaN, then we throw an error using throw new Error("Result is NaN")
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | srcArray = from([1, 2, 'A', 4]); obs = this.srcArray .pipe( map(val => { let result = val as number * 2; if (Number.isNaN(result)) { console.log('Errors Occurred in Stream') throw new Error("Result is NaN") } return result }), ); ngOnInit() { this.obs.subscribe( el => { console.log('Value Received ' + el) }, err => { console.log("Error caught at Subscriber " + err) }, () => console.log("Processing Complete.") ) } **** Output ***** Value Received 2 Value Received 4 Errors Occurred in Stream Error Caught at subscriber Error: Result is NaN |
We subscribe and start to receive the values from the obs
observable in the ngOnInit
method. When the observable stream throws an error, it invokes the error
callback. In the error
callback, we decide what to do with the error.
Note that once the observable errors out it will not emit any values neither it calls the complete
callback. Our subscription method will never receive the final value of 8.
Catch errors in the observable stream
Another option to catch errors is to use the CatchError
Operator. The CatchError Operators catches the error in the observable stream as and when the error happens. This allows us to retry the failed observable or use a replacement observable.
Using CatchError
Operator
To use CatchError operator, we need to import it from the rxjs/operators
as shown below
1 2 3 | import { catchError } from 'rxjs/operators' |
Syntax
The catchError is a pipeable operator. We can use it in a Pipe method similar to the other operators like Map, etc.
The catchError operator gets two argument.
The first argument is err
, which is the error object that was caught.
The second argument is caught
, which is the source observable. We can return it back effectively retrying the observable.
The catchError must return a new observable or it can throw an error.
Returning a new observable
The following examples shows the use of catchError
operator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | srcArray = from([1, 2, 'A', 4]); obs = this.srcArray .pipe( map(val => { let result = val as number * 2; if (Number.isNaN(result)) { console.log('Errors Occurred in Stream') throw new Error("Result is NaN") } return result }), catchError(error => { console.log('Caught in CatchError. Returning 0') return of(0); //return from(['A','B','C']) }) ); //Output Value Received 2 Value Received 4 Errors Occurred in Stream Caught in CatchError. Returning 0 Value Received 0 Observable Completed |
In the code above, the map emits the values 2 & 4, which is input to the catchError
. Since there are no errors, catchError
forwards it to the output. Hence the subscribers receive values 2 & 4.
The catchError
comes into play, when the map operator throws an error. The catchError
handle the error and must return a new observable (or throw an error). In the example above we return a new observable i.e. of(0)
. You can also emit any observable for example return from(['A','B','C'])
etc
You can also return the original observable. Just use the return this.obs;
instead of return of(0);
. But beware, It will result in an infinite loop.
The new observable is automatically subscribed and the subscriber gets the value 0
. The new observable now finishes and emits the complete
event.
Since the original observable ended in a error, it will never emit the the value 8.
Throws a new Error
catchError
can also throw an error. In the following example, we use the throw new Error(error)
to throw a JavaScript error. This error will propagate to the subscriber as shown in the example below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | obs = this.srcArray .pipe( map(val => { let result = val as number * 2; if (Number.isNaN(result)) { console.log('Errors Occurred in Stream') throw new Error("Result is NaN") } return result }), catchError(error => { console.log('Caught in CatchError. Throwing error') throw new Error(error) }) ); //OUTPUT Value Received 2 Value Received 4 Errors Occurred in Stream Caught in CatchError. Throwing error Error caught at Subscriber Error: Error: Result is NaN |
We can also make use of throwError
to return an observable. Remember that the throwError does not throw an error like throw new Error
but returns an observable, which emits an error immediately.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | obs = this.srcArray .pipe( map(val => { let result = val as number * 2; if (Number.isNaN(result)) { console.log('Errors Occurred in Stream') throw new Error("Result is NaN") } return result }), catchError(error => { console.log('Caught in CatchError. Throwing error') return throwError(error); }) ); //OUTPUT Value Received 2 Value Received 4 Errors Occurred in Stream Caught in CatchError. Throwing error Error caught at Subscriber Error: Result is NaN |
Retrying
You can also retry the observable using the Retry operator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | obs = this.srcArray .pipe( map(val => { let result = val as number * 2; if (Number.isNaN(result)) { console.log('Errors Occurred in Stream') throw new Error("Result is NaN") } return result }), retry(2), catchError((error,src) => { console.log('Caught in CatchError. Throwing error') throw new Error(error) }) ); //Output Value Received 2 Value Received 4 Errors Occurred in Stream Value Received 2 Value Received 4 Errors Occurred in Stream Value Received 2 Value Received 4 Errors Occurred in Stream Caught in CatchError. Throwing error Error caught at Subscriber Error: Error: Result is NaN |
The catchError
gets the source observable as the second argument. If we return it, it will get subscribed again effectively retrying the observable.
Ensure that you keep track of no of tries so that you can stop the observable after a few failed attempts. Otherwise, you may run into an infinite loop if the observable always emits an error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | obs = this.srcArray .pipe( map(val => { let result = val as number * 2; if (Number.isNaN(result)) { console.log('Errors Occurred in Stream') throw new Error("Result is NaN") } return result }), catchError((error,src) => { console.log('Caught in CatchError. Throwing error') this.count++; if (this.count < 2) { return src; } else { throw new Error(error) } }) ); |
excellent example