RxJS: Observable.never() keeps like subscription

2020-05-10 04:12发布

问题:

I'm using rxjs 5.5.6.

I've created this code in order to show the behavior:

Observable.of(1, 2)
    .do(a => {
        console.log(a);
        let d:string = null;
        let r = d.length;  // it raises an null exception
    })
    .catch(() => {
        console.log("error catched");  //exception is capture here
        return Observable.never();
    })
    .subscribe();

I was expecting that the output is:

1
error catched
2
error catched

However, the output is:

1
error catched

It means, the subscription terminates despite of an Observable.never() is returned on .catch(...) method chain.

Any ideas?

REAL CASE

this.subs = Observable
    .merge(this.$searchQuery, this.$lazyQuery)
    .do(() => this.loadingPage())
    .map(filter => this.buildURL(user, app, filter))
    .switchMap(url => this.service.getItemsFromService(url))
    .map(response => this.buildPage(response))
    .do(page => this.loadedPage(page))
    .catch(() => {
        this.loadedPage(pojo.Page.EMPTY);
        return Observable.never();
    })
    .takeUntil(this.$unsubscribe)
    .subscribe();

回答1:

You get the output

1 
error catched

because the first value throws an error in tap which is propagated through the onError channel which halts the sequence. With catch you catch this error and move on to the next sequence (never), but the first sequence (of(1,2)) is still halted. So after 1 throws an error 2 is never processed by tap.

After you return never in catch no value is emitted and the Observable never completes. If you add logs inside subscribe for the next, error and complete callbacks you will see that they are never executed.



回答2:

Yes all the explanation by @frido is correct. With that answer I would like to add:

If you want to capture any error occuring in any particular Observable itself(say an HTTP request) then you need to handle it in that particular erroring Observable.

  let correct = Observable.of("correct")
  let inCorrect = Observable.throw('inCorect')

  let obs = [inCorrect, correct];
  let handledObs = obs.map(eachObs => {
    return eachObs.catch((e) => {
      console.log("Individual handler, Good Catch!");
      return Observable.of("I am tampered");
    })
  })

  forkJoin(...handledObs)
  .do(a => {
      console.log(a);
  })
  .catch(() => {
      console.log("error catched");
      return Observable.never();
  })
  .subscribe(data => {
    console.log(`data`, data)
  },(e) => {
      console.log(`error`, e)
  });

}

See an example here: https://stackblitz.com/edit/angular-7mmhn7?file=src/app/app.component.ts


EDIT

However when I look at your code, it looks to me that you are trying to log something and the returned data may not have a length property, even in that case you want to proceed with the stream. If this is true then you can add a simple try catch under do()

  from([1, 2])
  .do(a => {
      try {
        console.log(a);
        let d:string = null;
        let r = d.length;
      } catch(e) {
        console.log(`catched under do`, e)
      }
  })
  .catch(() => {
      console.log("error catched");
      return Observable.of('catched');
  })
  .subscribe(data => {
    console.log(`data`, data)
  },(e) => {
      console.log(`error`, e)
  });

Here is an example: https://stackblitz.com/edit/angular-bcrava?file=src/app/app.component.ts