RxJS:嵌套渔获量与原始渔获量

时间:2019-05-09 08:57:16

标签: angular typescript rxjs

我发现了我正在努力找出的一种行为,但我绝对失去了思路。

当前工作可观察的是:

Observable
    .merge(this.eager$, this.lazy$)
    .map(() => this.buildURL())
    .switchMap(url =>
        this.service.getItemsFromSservice(url)
        .map(response => this.buildPage(response))
        .catch(() => Observable.of(pojo.Page.EMPTY))
    );

我的意思是,当由于http通讯错误而引发异常时,该异常将由嵌套的.catch(...)接收并返回一个空的Page,并且订阅继续进行,不会出现任何其他问题。

但是,如果我将嵌套的.map(...).catch(...)移到“主线”链中,则订阅无效

Observable
    .merge(this.eager$, this.lazy$)
    .map(() => this.buildURL())
    .switchMap(url => this.service.getItemsFromSservice(url))
    .map(response => this.buildPage(response))
    .catch(() => Observable.of(pojo.Page.EMPTY)
);

使用上述代码,当出现http错误时,订阅将终止。 为什么?

其中:

this.eager$ = Rx.Observable.fromEvent(this.searchButton, 'click');
this.lazy$ = Rx.Observable.fromEvent(this.lazyButton, 'click');

2 个答案:

答案 0 :(得分:1)

因为当一个可观察对象发出一个错误时,它永远不会再发出任何东西。错误状态是终端状态。

在第一个代码段中,每次外部可观测对象(由merge()创建)发出时,switchMap()都会创建一个新的内部可观测对象。如果此内部观测值发出错误,则将捕获此错误并发出空白页而不是错误。内部可观测对象已达到其最终状态,将不再发出任何信号,但是外部可观测对象从未发出任何错误。如果外部可观测对象发出新事件,则switchMap()会再次创建一个全新的内部可观测对象。

在第二个代码段中,如果内部可观察对象发出错误,则外部可观察对象的确会发出错误,因为它没有被捕获并被空页面替换。因此,外部可观测对象到达其终端,即错误状态,并且不再发射。

在同步世界中,您可以将其视为两者之间的区别:

for (let i = 0; i < 1000; i++) {
  try {
    doSomething(i);
  }
  catch (e) {
    // too bad
  }
  console.log(i);
}

try {
  for (let i = 0; i < 1000; i++) {
    doSomething(i);
  }
  console.log(i);
}
catch (e) {
  // too bad
}

在第一个代码段中,即使doSomething()抛出异常,也将打印从0到1000的所有数字,因为捕获并忽略了doSomething()抛出的异常。

但是,在第二个异常中,将引发异常,从而中断外部循环。这意味着如果doSomething()抛出异常,并非所有数字都会被打印。

答案 1 :(得分:0)

问题是主流已经出错,根据rxjs的说法,它不再可用。嵌套的catch有效,因为它只是说getItemsFromSservice有错误,并且它将继续侦听this.eager$this.lazy$的发射。

也许这行得通,但根本没有测试过:

const page$ = Observable
    .merge(this.eager$, this.lazy$)
    .map(() => this.buildURL())
    .switchMap(url => this.service.getItemsFromSservice(url))
    .map(response => this.buildPage(response))
);

this.page$
  .catch(() => page$.startWith(pojo.Page.EMPTY))
  .subscribe((page) => console.log(page));