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
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))
}
I have a PR in which the operator onBackpressureLatest()
should have the expected behavior, but you need concurrency and can use observeOn
as usual.