Most of the common operators are either about the transformation, or combination, or filtering, but now we're going to see a new category of operators, error handling operators, of which catch is the most common one or the most important.
In a real application, things may go wrong. Here's an example of an Observable of strings, foo, which we're mapping to their uppercase to get Observable bar. Now, let's say foo would emit a number instead of a string. Then, when we try to map that number to its uppercase, that doesn't really work, so we get an error here at the bottom.
Let's see that happening. A, B, C, and D, and then an error because numbers x don't have toUpperCase as a function.
At this point, let's remember that once an error happens, it means that the Observable has terminated. Nothing will happen after that. What catch allows us to do is replace this error with another Observable, and that's really the argument that we give to catch is a function that takes an error.
Here, you're supposed to return an Observable to mean what to replace the error with. Here, we're going to replace the error with an Observable of just the string "Z," and then it completes.
If we plot that, we see we're going to take an error like this, and we're going to replace it with an observable that has just "Z," and then it completes. We're going to say A, B, C, and D, and then Z, and complete. That's our result Observable. Then we see A, B, C, D, and Z, and done.
You can also be a bit more creative here. Instead of replacing the error with an Observable that has an emission, you can also just have the complete notification. That would be Observable empty. Observable empty just emits "complete" and nothing else. That's why the error will be replaced with "complete." Then we get A, B, C, D, and complete.
You can also replace the error with a never. That means that here, it would just never emit anything. It would just emit A, B, C, D, and then nothing after that.
The replacing logic we give to catch takes, also, a second argument, and that would be the output Observable. Now, what does that actually refer to? Output Observable is not bar, but output Observable is actually the result Observable. This allows you to give a retry behavior to catch.
Let's see that in action. Catch will replace an error with the output Observable itself, which is this. This is the output Observable. It replaces that error with the output Observable, and then that error will definitely happen after D again, so then it will replace again, and it will keep on doing this.
Essentially, what we get is every time an error happens, restart the whole execution and get the same things happening again.
In this case, of course, it's going to keep on retrying this forever, because definitely errors happen after D, so it will keep on retrying forever. There are cases where this retry behavior makes more sense. Let's take a look at one of those.
Here, we have Observable foo, which is an Observable of random numbers emitted every half a second. These random numbers are in the range of zero and one. Then, we have a map operation that checks if that number is less than half, then it just sends that number, but if the number is between half and one, then it will throw an error.
In this case, here, if this one was 07, for instance, then that will be an error thrown on bar. When we run this, because it's random, if this first one was 07, then it just starts with, "An error was thrown." It may take a while, then it throws a 08. This behavior is random.
What we want to do with catch is be able to say whenever an error happens, just restart this whole process all over again. Basically, we get this, and we replace the error with itself, and we just keep on going.
Let's say that here happened an error, and it's just going to replace this whole error with that Observable that just keeps on emitting random numbers. That's what the result will look like.
Let's run that. Sometimes when it sees an error, it just restarts the Observable. Then, we get just numbers that are between 0and 05.
Catch is useful not just to replace a single error with an Observable, but to replace an error with a retry behavior to go back and resubscribe.