简体   繁体   中英

RxJS how to ignore an error with catch and keep going

Hi I have the following code and I would like to know how to prevent the main (upstream) Observable from getting deleted when an error is thrown.

How can I change the following code so that all numbers expect '4' get displayed?

I am looking for a general pattern solution that would work in other cases with different operators. This is the simplest case I could come up with.

const Rx = require('rxjs/Rx');

function checkValue(n) {
  if(n === 4) {
    throw new Error("Bad value");
  }
  return true;
}
const source = Rx.Observable.interval(100).take(10);

source.filter(x => checkValue(x))
  .catch(err => Rx.Observable.empty())
  .subscribe(v => console.log(v));

You will want to keep the source observable running, but if you let the error happen on the main event stream it will collapse the entire observable and you will no longer receive items.

The solution involves creating a separated stream where you can filter and catch without letting the upstream pipe collapse.

const Rx = require('rxjs/Rx');
function checkValue(n) {
  if(n === 4) {
    throw new Error("Bad value");
  }
  return true;
}
const source = Rx.Observable.interval(100).take(10);

source
  // pass the item into the projection function of the switchMap operator
  .switchMap(x => {
     // we create a new stream of just one item
     // this stream is created for every item emitted by the source observable
     return Observable.of(x)
       // now we run the filter
       .filter(checkValue)
       // we catch the error here within the projection function
       // on error this upstream pipe will collapse, but that is ok because it starts within this function and will not effect the source
       // the downstream operators will never see the error so they will also not be effect
       .catch(err => Rx.Observable.empty());
     })
     .subscribe(v => console.log(v));

You could also use the second argument passed into the catch selector to restart the observable source, but this will start it as though it hasn't run before.

const Rx = require('rxjs/Rx');

function checkValue(n) {
  if(n === 4) {
    throw new Error("Bad value");
  }
  return true;
}
const source = Rx.Observable.interval(100).take(10);

source.filter(x => checkValue(x))
  .catch((err, source) => source)
  .subscribe(v => console.log(v));

But this does not achieve the desired effect. You will see a stream that emits 1..3 repeatedly until the end of time... or you shutdown the script. Which ever comes first. (this is essential what .retry() does)

You need to use a flatMap operator where you will do the filtering. In the flatMap in this example I'm using Observable.if() to do the filtering as it guarantees me that I'm returning observables all the time. I'm sure you can do it other ways but this is a clean implementation for me.

const source = Rx.Observable.interval(100).take(10).flatMap((x)=>
    Rx.Observable.if(() => x !== 4, 
    Rx.Observable.of(x),
    Rx.Observable.throw("Bad value"))
    .catch((err) => {
        return Rx.Observable.empty()
    })
);

source.subscribe(v => console.log(v));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM