如何在Kotlin协同例程接收频道上实现RxJava的combineLatest?

时间:2018-03-11 00:40:33

标签: kotlin reactive-programming kotlinx.coroutines

我需要在.combineLatest()

上实施以下ReceiveChannel扩展功能
suspend fun <A, B, R> ReceiveChannel<A>.combineLatest(
    otherSource: ReceiveChannel<B>,
    context: CoroutineContext = Unconfined,
    combineFunction: suspend (A, B) -> R
): ReceiveChannel<R> = produce(context) {
    // ?
}

我希望它能像RxJava&#39; s combineLatest()一样发挥作用。

我该怎么做?

编辑:到目前为止,我有这个,但它不起作用。 sourceB.consumeEach{ }块永远不会被删除。

suspend fun <A, B, R> ReceiveChannel<A>.combineLatest(
    otherSource: ReceiveChannel<B>,
    context: CoroutineContext = Unconfined,
    combineFunction: suspend (A, B) -> R
): ReceiveChannel<R> = produce(context) {

    val sourceA: ReceiveChannel<A> = this@combineLatest
    val sourceB: ReceiveChannel<B> = otherSource

    var latestA: A? = null
    var latestB: B? = null

    sourceA.consumeEach { a ->
        latestA = a
        if (latestA != null && latestB != null) {
            send(combineFunction(latestA!!, latestB!!))
        }
    }

    sourceB.consumeEach { b ->
        latestB = b
        if (latestA != null && latestB != null) {
            send(combineFunction(latestA!!, latestB!!))
        }
    }
}

我还想确保当此函数返回的ReceiveChannel<R>关闭(取消订阅)时,我想确保父通道正确关闭。

2 个答案:

答案 0 :(得分:0)

这就是诀窍!我仍然感到困惑,为什么我可以将一个.consumeEach{ }嵌套在另一个.consumeEach { }中 - 这似乎不直观。

suspend fun <A, B, R> ReceiveChannel<A>.combineLatest(
    otherSource: ReceiveChannel<B>,
    context: CoroutineContext = Unconfined,
    combineFunction: suspend (A, B) -> R
): ReceiveChannel<R> = produce(context) {

    val sourceA: ReceiveChannel<A> = this@combineLatest
    val sourceB: ReceiveChannel<B> = otherSource

    val latestA = AtomicReference<A>()
    val latestB = AtomicReference<B>()

    var aInitialized = false
    var bInitialized = false

    sourceA.consumeEach { a ->
        latestA.set(a)
        aInitialized = true
        if (aInitialized && bInitialized) {
            send(combineFunction(latestA.get(), latestB.get()))
        }

        launch(coroutineContext) {
            sourceB.consumeEach { b ->
                latestB.set(b)
                bInitialized = true
                if (aInitialized && bInitialized) {
                    send(combineFunction(latestA.get(), latestB.get()))
                }
            }
        }
    }
}

答案 1 :(得分:0)

我知道这是一个老问题,但这是一个建议:

我建议使用.zip()而不是嵌套.consumeEach。查看文档here

可能的解决方案sourceA.zip(sourceB).consumeEach{}会生成类型为Pair的项目。