The use of onError gives us a much better experience overall, but it isn't very flexible.
Let's imagine a different scenario, where we have an observable retrieving data from the network. What if, when this observer fails, we would like to present the user with a cached value instead of an error message?
This is where the catch combinator comes in. It allows us to specify a function to be invoked when the observable throws an exception, much like OnError does.
Differently from OnError, however, catch has to return a new observable that will be the new source of items from the moment the exception was thrown:
(rx/subscribe (->> (exceptional-obs)
(rx/catch Exception e
(rx/return 10))
(rx/map inc))
(fn [v] (prn-to-repl "result is " v)))
;; "result is " 11
In the previous example, we are essentially specifying that, whenever exceptional-obs throws, we should return the value 10. We are not limited to single values, however. In fact, we can use any observable we like as the new source:
(rx/subscribe (->> (exceptional-obs)
(rx/catch Exception e
(rx/seq->o (range 5)))
(rx/map inc))
(fn [v] (prn-to-repl "result is " v)))
;; "result is " 1
;; "result is " 2
;; "result is " 3
;; "result is " 4
;; "result is " 5