不要在出错时停止Observable

时间:2014-06-03 10:59:56

标签: scala reactive-programming observable rx-java

这是我目前拥有的Scala代码:

  

val b = Observable.interval(1秒).map(n => if(n%2 == 1)   抛出新的异常,否则n * n)

     

b.subscribe(n => println(n),e => println(" error"),()=>   的println("完成&#34))

这是我的输出:

  

0
  错误

如何修改我的Observable,以便它在每次出错后继续运行,并且我会输出这样的输出:

0
error
2
error
4
...

2 个答案:

答案 0 :(得分:1)

您可以使用各种错误处理程序之一。 我认为在你的情况下onErrorFlatMap可能是正确的选择:

Wiki

JavaDoc

不幸的是,onErrorFlatMap不是(从版本0.19.0开始)scala api的一部分。

答案 1 :(得分:0)

我有同样的问题,并且对于Scala Rx没有onErrorFlatMap感到失望。所以我抓了一针 我自己实现这个功能。

我的解决方案如下所示(参见解决方案)。关键方法是这个:

  def recover[T](target: Observable[T]): Observable[Try[T]] = {
      target.map { Success(_) }.
        onErrorResumeNext(
          (err: Throwable) => Observable.just(Failure(err)) ++ recover(target)
        )
  }

'恢复'方法的详情

'恢复'的第一个参数是你想要继续挤压事件的观察结果 它会引发异常。我尝试了各种其他方法,但这是唯一有效的方法 我。我最初期望Scala Rx的onErrorReturn将任何错误映射到我的恢复所规定的值 功能,然后继续前进,但我错过了'可观察合同'的全部内容,就是这样 一个Observable需要在onCompleted或OnError之后停止发送任何进一步的事件。任何Observable 在错误被标记为“病态”之后继续喷出事件(并由礼貌社会适当避开), 如下所述:https://github.com/ReactiveX/RxJava/wiki/Phantom-Operators#onerrorflatmap

成功的onNext调用的有效负载包含在Success()中,而任何异常都将被处理 通过onErrorResumeNext,它将从(1)一个Observable包装创建连接的Observable流 错误,以及(2)在递归调用中包装的目标实例以进行恢复。我最初 担心无限递归..但这一切都很顺利。

限制

我应该提一下,在原始海报问题的情况下 - 使用Observable.interval,这个 不会很好,因为recover(target)将是原始的Observable.interval,它将启动 从一开始就散发出来,所以你永远不会取得进步。对于像间隔这样的东西,你会的 必须编写自己的基于计时器的间隔,可以重新启动。有希望的例外值 给你足够的信息告诉你需要重新开始的价值。

A SOLUTION




object RecoverFromExceptionsInObservable extends App {

  import rx.lang.scala._
  import scala.language.postfixOps
  import scala.util.{Try, Failure, Success}


  val MILLISECS = 500L
  var tickCount = 0

  /**
   * There is a bug in this code which we will ignore for the simple purpose of
   * this test. The bug is that timers are never stopped and cleaned up.
   */
  def getTickObservable(): Observable[Int] = {
    @volatile var subscribers: Set[Observer[Int]] = Set.empty

    val t = new java.util.Timer()
    val task = new java.util.TimerTask {
      def run() = {
        subscribers.foreach(s => s.onNext(tickCount))
        tickCount += 1
      }
    }
    t.schedule(task, 0L, MILLISECS)

    Observable.create[Int] {
      (obs: Observer[Int]) => {
        subscribers = subscribers + obs
        Subscription {
          subscribers = subscribers - obs
        }
      }
    }
  }

  def recover[T](target: Observable[T]): Observable[Try[T]] = {
      target.map { Success(_) }.
        onErrorResumeNext(
          (err: Throwable) => Observable.just(Failure(err)) ++ recover(target)
        )
  }


  val stream1 = getTickObservable() map { item =>
    if (item % 2 == 0) throw new RuntimeException(s"error on $item") else item
  }

  recover(stream1).subscribe(
    term => {
      println(s" ${Thread.currentThread().getName()}  onNext: $term")
    },
    t => {
      println("in error callback")
      println(s" ${Thread.currentThread().getName()}  onError: $t")
    },
    () => println(s" ${Thread.currentThread().getName()} subscriber complete")
  )
}

以下是上述代码的部分输出:

 Timer-0  onNext: Success(1)
 Timer-0  onNext: Failure(java.lang.RuntimeException: error on 2)
 Timer-0  onNext: Success(3)
 Timer-0  onNext: Failure(java.lang.RuntimeException: error on 4)
 Timer-0  onNext: Success(5)
 Timer-0  onNext: Failure(java.lang.RuntimeException: error on 6)

我不希望这个答案永远持续下去,所以我跳过了一些关于解决这个问题的替代方法的细节,如果你有兴趣,可以阅读here