Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxJS - Catch and carry on

I'm struggling to handle errors with Rx in the manner I expected.

When I have an Observable (for example, from a click stream) and an exception occurs I want to catch it but carry on. All the solutions I have tried catch the error then end the Observable. Is there a way to catch and carry on?

As an example the Observable below will emit "1", then "Error", but never "3".

var range = Rx.Observable.range(1,3)
    .map(function(i){
      if(i === 2){
        throw "Error";
      } else {
        return i;
      }
    })
    .catch(function(e){
      return Rx.Observable.return(e)
    });

range.subscribe(function(i){
  console.log(i)
});
like image 561
MaxWillmott Avatar asked May 22 '15 09:05

MaxWillmott


1 Answers

While your expected behavior cannot be achieved because of the Observable contract (OnNext)* (OnCompleted|OnError), there are practical ways of properly working around this by introducing a hot Observable.

let hotRange = Rx.Observable.range(1,3).publish();

let safeRange = hotRange
  .map(function (i) {
    if (i === 2) {
      throw "Error";
    } else {
      return i;
    }
  })
  .retry();

safeRange.subscribe(i => console.log(i));
hotRange.connect();

See the JSBin. The range Observable you mentioned in the question is a cold Observable. It behaves as a movie, so if an error happens and we resubscribe, we need to subscribe from the beginning of the "movie", that is, 1 then "Error".

You probably had an implicit assumption that Rx.Observable.range(1, 3) was a live Observable, i.e., "hot". Since it isn't, I made hotRange above using publish(). This way, it will emit its events independently of its subscribers. If we want to be able to "carry on" after an error, we need our source ("hotRange") to be without errors. That's why range.map( ) isn't the hot Observable. retry() will catch errors on hotRange.map( ) and replace it with hotRange.map( ). Because hotRange is hot, every execution of retry() will be different, since it doesn't remember previous values emitted by hotRange. So when the error caused by 2 is replaced by hotRange.map( ) in the retry, hotRange will subsequently emit 3, and pass the map function without errors.

like image 126
André Staltz Avatar answered Oct 31 '22 19:10

André Staltz