RxJS – Part 5 – RxJS error handling
All posts in RxJS series:
- RxJS – Part 1 – Introduction
- RxJS – Part 2 – Observable
- RxJS – Part 3 – Hot and Cold Observable
- RxJS – Part 4 – Operators
- RxJS – Part 5 – RxJS error handling
- RxJS – Part 6 – Chat application with RxJS
Error handling
In the first post we saw that we have 3 main methods on Observer object: next
, error
and complete
. Lets focus on error() method. When does it gets called? Lets see the code example.
For that scenario we will use almost the same code that we used in second post when we used Observable.create()
. We will have only few minor changes. We will add another word to our words
array. Furthermore, we will add a condition in the for loop
to execute observer.error()
method once we run into ‘badword‘.
As a result we get the following output:
We can see from the output that we have an uncaught exception and once again complete() did not run. Also notice that last console.log
never happened. That’s because we had an uncaught exception.
What if we include error method when subscribing to our Observable source?
In this case we get the following output:
From this we can notice that error method did execute. However, complete method did not execute. Calling observer.error() is similar to raising exception in regular code and will cause Observable to stop processing the array. Furthermore, we can see that last console.log
did run in this case.
If we leave all the code same but we replace observer.error call with throw new Error:
This time we will get an uncaught exception and last line – console.log('after subscribe')
will not run!
If you want to play around with code in editor and see what happens you can find the plunker version at this link.
Catch me please!
We saw we can use throw
with RxJS but what about catch? We cannot use try {} catch {}
. It would be useless since we are dealing with asynchronous functions and operators. By the time most of the code executes we will already be outside of the try/catch block.
As we saw if an unexpected error happens in one of the operators we get an uncaught exception and things break:
Now the output:
Now, that is something that makes every developer sad. How can we handle this? Just like Promises have .catch
method we also have the catch
operator in RxJS.
Lets see the output now:
There is no error now! However, we did not get all the values. Observable execution completed after second word. Maybe we do not want to handle it like this? Lets assume that error that came from map()
was an exception that happened in our code and was not so obvious. Maybe we want to throw our own error from catch
operator?
Now we get the following output:
In both cases our Observable stops at error. That is because once an error is encountered in Observable it basically unsubscribes and will not continue on to next element.
Can we do better?
Maybe we are using RxJS to get some data from our API and it could be unavailable for few seconds or so. It would be great if we could retry our request one more time? or few more times? Or maybe we want to try again in 5 seconds? Worry not! RxJS has retry
and retryWhen
operators and they can do all of that!
And the output:
That seems quite crude. Could be useful in some cases but we are usually gonna opt out for something more sophisticated. That is why RxJS has retryWhen
operator.
retryWhen
takes an Observable of errors as its only parameter and it expects us to return an Observable. And how does it know when to retry? It is quite simple actually. When that Observable that we return from retryWhen
emits a value. That is what triggers it. After that Observable on which retryWhen
is operating on will try again its logic that caused the error in first place.
And the output:
That might not be what we want. Maybe we want to error out after few times:
We are retrying for our logic to complete 3 times. Every time there is a delay of 2 seconds.
We get the following output:
We could extract the method inside of our retryWhen
operator and implement it as reusable function with different approach. We could make few of those and use them in our application. Once a method is extracted and just forwarded to operator it definitely makes code look better as well.
Summary
We saw how we can deal with errors with RxJS. Hopefully you understood what is the difference when we are dealing with an error that happened purposefully and an error that was not properly handled.
If we get an unhandled error neither complete
nor error
method will execute. However, if handled error occurs either error
or complete
method will execute but never both of them!
Also, we saw how we can use retry
operator and its more sophisticated cousin retryWhen.
That is all for now. In the next post we will talk about real world use cases and see RxJS in action.