即使抛出错误,如何使observable继续发出值

时间:2016-12-27 14:19:11

标签: rxjs rxjs5

抛出并替换错误。但随后执行结束。如何使observable发出10个元素?

const Rx = require('rxjs/Rx')

Rx.Observable.interval(1000)
  .map((i) => {
    if (i === 2) throw(new Error('omg'))
    return i
  })
  .take(10)
  .catch((err) => {
    return Rx.Observable.of('ok, we caught an error, but we don\'t want to exit')
  })
  .do(console.log, console.error)
  .subscribe()

3 个答案:

答案 0 :(得分:1)

您可以提供一个处理错误的函数并返回一个observable。您需要使用flatMap,因为您将使用更高阶的功能。

function handleError(cb){
    return (val) => {
        try{
            return Rx.Observable.of(cb(val));
        }catch(err){
            console.error(`${err}`);
            return Rx.Observable.empty();
        }
    }
}

Rx.Observable.interval(1000)
    .flatMap(handleError(i => {
        if (i === 2) throw(new Error('omg'))
        return i;
    }))
    .take(10)
    .do(console.log)
    .subscribe()

// emits
// 0
// 1
// "Error: omg"
// 3
// 4
// 5
// 6
// 7
// 8
// 9
// 10

jsbin example

另一个不太适合您的示例代码但又值得一提的例子,RxJS的首席开发人员Ben Lesh在一个名为 On The Subject Of Subjects (in RxJS) 的帖子中触及了这个问题。中间有一段称为“RxJS中的陷阱”。

  

[...]由于Rx observables没有“陷阱”错误,我们可能遇到一些问题   这里奇怪的行为。错误“陷阱”是我自己的行为   嘲笑实施的承诺,但在多播场景中它可能   是正确的举动。当我说Rx observable没有时,我的意思是什么   “陷阱”错误基本上是当错误渗透到结束时   观察者链,如果错误未处理,它将被重新抛出。

以下是该部分的一个代码示例(最简单但效果最差):

const source$ = Observable.interval(1000)
  .share()
  .observeOn(Rx.Scheduler.asap); // magic here
const mapped$ = source$.map(x => {
  if (x === 1) {
    throw new Error('oops');
  }
  return x;
});
source$.subscribe(x => console.log('A', x));
mapped$.subscribe(x => console.log('B', x));
source$.subscribe(x => console.log('C', x));
// "A" 0
// "B" 0
// "C" 0
// "A" 1
// Uncaught Error: "oops"
// "C" 1
// "A" 2
// "C" 2 
// "A" 3
// "C" 3
// ... etc

jsbin example

答案 1 :(得分:0)

我不是RxJS大师,但我想尝试回答这个问题。

使用RxJS抛出错误会终止observable。因此,您无法恢复它,但只能尝试重试/重复观察。

如果您不想重播错误,并且只能使用原始的10个元素,那么您可以return null代替throw new Errorfilter(x => x) take(10)

否则,您可以使用retryWhen在出错时重复观察。请注意,这将占用2个项目,失败,然后它将重新开始重复0,1,......它在2次失败后突然出现,但仍然只需要10个项目。

Rx.Observable.interval(1000)
  .map((i) => {
    if (i === 2) throw(new Error('omg'))
    return i
  })
  .retryWhen((errors) => errors.scan(
    (errorCount, err) => {
        if(errorCount >= 2) {
            throw err;
        }

        return errorCount + 1;
    }, 0)
  )
  .take(10)
  .catch((err) => {
    return Rx.Observable.of('ok, we caught an error, but we don\'t want to exit')
  })
  .subscribe((x) => console.log('result', x))

您也可以使用repeat在观察完成后继续重复。这可能不是你想要的,但无论如何我想把它作为一个选项展示给你。您需要注意放置的位置,并且还“计算”可观察到的捕获量。

Rx.Observable.interval(1000)
  .map((i) => {
    if (i === 2) throw(new Error('omg'))
    return i
  })
  .catch((err) => {
    return Rx.Observable.of('ok, we caught an error, but we don\'t want to exit')
  })
  .repeat()
  .take(10)
  .subscribe((x) => console.log('result', x))

答案 2 :(得分:0)

可观察合约为OnNext*(OnError|OnCompleted)+

一旦序列结束,下游运营商必须取消订阅。您只能重新订阅管道。如果您的observable很冷,那么您可以使用retry运算符重新订阅。

observable
.retry()
.take(10)
.subscribe()