Nick
04/13/2019, 4:30 AMthrottleLatest RX operator. I have the following:
/**
* Emits the next item emitted by a reactive source, then periodically emits the latest item
* (if any) when the specified timeout elapses between them.
*
* <https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables#throttlelatest>
*/
fun <E> ReceiveChannel<E>.throttleLatest(
wait: Long = 1_000,
context: CoroutineContext = Dispatchers.Default
): ReceiveChannel<E> = GlobalScope.produce(context) {
var currentData: E? = null
val tick = ticker(initialDelayMillis = 0, delayMillis = wait)
while (!isClosedForReceive) {
select<Unit> {
tick.onReceiveOrNull {
currentData?.let {
send(it)
}
}
onReceiveOrNull {
if (currentData == null) {
it?.let {
send(it) // send first time
}
}
currentData = it
}
}
}
tick.cancel()
}
My unit test:
/**
* -A----B-C-------D--------|-->
* -a------c---1s
* ---d------1s
* -|-->
* -A---------C-----------D-|-->
*/
@Test
fun throttleLatestTest() = runBlocking {
val channel = Channel<String>()
launch {
channel.send("A") // Emit the first item
channel.send("B") // Not the latest, so won't emit
channel.send("C") // Since this is the last one before the 1s delay, this will be sent
delay(1000)
channel.send("D") // Emit this on the next tick
delay(1000) // to keep the thread alive longer
channel.close()
}
val expected = listOf<String>("A", "C", "D")
val actual = channel.throttleLatest(1000).toList()
println(actual)
assertEquals(expected, actual)
}
This works fine, but I was wondering if anyone could see any improvements on it? I feel like it’s just not perfect.