How to implement observeLatestOn in RxJava (RxScala)?

前端 未结 2 945
悲哀的现实
悲哀的现实 2021-01-21 18:52

I\'m trying to implement the ObserveLatestOn operator in RxJava (actually, RxScala).

This operator is useful, when we\'ve got a fast producer and a slow subscriber, but

相关标签:
2条回答
  • 2021-01-21 19:41

    I recommend that using lift to implement this operator. Here is my solution:

    package object ObservableEx {
    
      implicit class ObserveLatestOn[T](val o: Observable[T]) {
    
        def observeLatestOn(scheduler: Scheduler): Observable[T] = {
          o.lift { (child: Subscriber[T]) =>
            val worker = scheduler.createWorker
            child.add(worker)
    
            val parent = new Subscriber[T] {
    
              private val lock = new AnyRef
    
              // protected by "lock"
              private var latest: Notification[T] = null
              // protected by "lock"
              // Means no task runs in the worker
              private var idle = true
    
              private var done = false
    
              override def onStart(): Unit = {
                request(Long.MaxValue)
              }
    
              override def onNext(v: T): Unit = {
                if (!done) {
                  emit(OnNext(v))
                }
              }
    
              override def onCompleted(): Unit = {
                if (!done) {
                  done = true
                  emit(OnCompleted)
                }
              }
    
              override def onError(e: Throwable): Unit = {
                if (!done) {
                  done = true
                  emit(OnError(e))
                }
              }
    
              def emit(v: Notification[T]): Unit = {
                var shouldSchedule = false
                lock.synchronized {
                  latest = v
                  if (idle) {
                    // worker is idle so we should schedule a task
                    shouldSchedule = true
                    // We will schedule a task, so the worker will be busy
                    idle = false
                  }
                }
                if (shouldSchedule) {
                  worker.schedule {
                    var n: Notification[T] = null
                    var exit = false
                    while (!exit) {
                      lock.synchronized {
                        if (latest == null) {
                          // No new item arrives and we are leaving the worker, so set "idle"
                          idle = true
                          exit = true
                        } else {
                          n = latest
                          latest = null
                        }
                      }
                      if (!exit) {
                        n.accept(child)
                      }
                    }
                  }
                }
              }
            }
    
            child.add(parent)
    
            parent
          }
        }
      }
    
    }
    

    And a unit test

    import ObservableEx.ObserveLatestOn
    
    @Test
    def testObserveLatestOn(): Unit = {
      val scheduler = TestScheduler()
      val xs = mutable.ArrayBuffer[Long]()
      var completed = false
      Observable.interval(100 milliseconds, scheduler).take(10).observeLatestOn(scheduler).subscribe(v => {
        scheduler.advanceTimeBy(200 milliseconds)
        xs += v
      },
        e => e.printStackTrace(),
        () => completed = true
      )
      scheduler.advanceTimeBy(100 milliseconds)
      assert(completed === true)
      assert(xs === List(0, 2, 4, 6, 8))
    }
    
    0 讨论(0)
  • 2021-01-21 19:51

    I have a PR in which the operator onBackpressureLatest() should have the expected behavior, but you need concurrency and can use observeOn as usual.

    0 讨论(0)
提交回复
热议问题