如何在RxJava(RxScala)中实现observeLatestOn?

时间:2015-04-30 12:02:15

标签: scala reactive-programming rx-java

我正在尝试在RxJava中实现ObserveLatestOn运算符(实际上是RxScala)。

当我们有一个快速生产者和一个慢速订阅者时,这个运营商很有用,但订阅者并不关心在消费项目时丢失的任何物品。

大理石图:

--1---2---3----------5------6--7-8-9------|
--1=========>3===>---5=======>6======>9==>|

=字符表示订阅者正在执行的长时间工作,>字符表示刚完成的工作。正如使用的规范示例所示,想象一些需要显示的数据的生成者,以及作为订阅者的数据的屏幕渲染器。渲染需要很长时间,但我们不需要渲染屏幕上的每一步,只是最后一步非常好。

在上面的大理石图中,生产者发出信号1.订户开始处理它,并且需要很长时间。同时,生产者发出2和3,并且在此之后订户完成工作。它看到生产者发出的最后一项是3,所以它开始处理它。这很快,同时没有生产新项目,因此订户可以休息。然后,5到达,故事以同样的方式继续。

我花了好几个小时试图实现这个看似简单的操作符,但我仍然不满意。运算符的本质表明它应该是异步的,它应该在不同的调度程序上发出它的项目。但与此同时,我当然不希望工作人员占用线程而没有工作要做。

这是我到目前为止所提出的:

def observeLatestOn[T](o: Observable[T], scheduler: Scheduler): Observable[T] = {
  @volatile var maybeNextItem: Option[Notification[T]] = None
  @volatile var isWorkScheduled = false
  val itemsQueueLock = new Object()

  Observable(subscriber ⇒ {
    def signalToSubscriber(materializedItem: Notification[T]): Unit = {
      materializedItem match {
        case OnNext(item) ⇒ subscriber onNext item
        case OnError(error) ⇒ subscriber onError error
        case OnCompleted ⇒ subscriber.onCompleted()
      }
    }

    def queueItem(item: Notification[T]): Unit = {
      val worker = scheduler.createWorker

      val shouldScheduleWork = itemsQueueLock synchronized {
        val result = !isWorkScheduled
        maybeNextItem = Some(item)
        isWorkScheduled = true
        result
      }

      if (shouldScheduleWork) {
        worker.scheduleRec {
          val maybeNextItemToSignal = itemsQueueLock synchronized {
            val result = maybeNextItem
            if (result.isEmpty) {
              worker.unsubscribe()
              isWorkScheduled = false
            }
            maybeNextItem = None
            result
          }

          maybeNextItemToSignal foreach signalToSubscriber
        }
      }
    }

    o.takeWhile(_ ⇒ !subscriber.isUnsubscribed).subscribe(
      next ⇒ queueItem(OnNext(next)),
      error ⇒ queueItem(OnError(error)),
      () ⇒ queueItem(OnCompleted)
    )
  })
}

它似乎有用,但我不相信没有竞争条件或死锁。此外,我不确定解决方案是否可以更简单。我也一直在考虑另一种方法,比如

  • 巧妙地使用OperatorDebounceWithSelector
  • 一次只能请求一项,observeOnonBackpressureBuffer(1)
  • 的可观察组合

我也不知道如何为此编写确定性单元测试。与scheduleRec一起使用时,TestScheduler计划的工作不能被中断,我需要使用一个真正适用于不同线程的调度程序。我发现很难为多线程代码的竞争条件编写正确的单元测试。

所以,问题仍然存在:我的解决方案是否正确?有没有更简单,更好或更正确的方法?以及如何测试它的正确性?

1 个答案:

答案 0 :(得分:0)

我有一个PR,其中运算符onBackpressureLatest()应具有预期的行为,但您需要并发,并且可以像往常一样使用observeOn