kotlin 为什么在启动内部绑定“onEach”会导致“MutableSharedFlow”事件丢失?

0s7z1bwu  于 2022-12-23  发布在  Kotlin
关注(0)|答案(1)|浏览(233)

本测试通过:

@Test
fun passingTest() {
    val emitter = MutableSharedFlow<Int>();
    var total = 0;

    val handler = emitter.onEach {  // <--- onEach bound outside of `launch`
        total += it;
        println("-> $it : total: $total")
    }

    GlobalScope.launch {
        handler.collect()
    }

    runBlocking {
        for (i in 1..11) {
            emitter.emit(1);
        }
    }

    assert(total == 11);
}

但是,如果我将.onEach移到launch中,它将不再工作:
x一个一个一个一个x一个一个二个x
发生了什么事?为什么我的onEach处理程序在连接到启动范围内时丢失了?

carvr3hs

carvr3hs1#

因此,我自己尝试了这两个测试用例,记录了GlobalScope.launchrunBlocking的块。

GlobalScope.launch {
    println("launch")
    handler.collect()
}

runBlocking {
    for (i in 1..11) {
        println(i)
        emitter.emit(1);
    }
}
println(total)

此测试用例在每次运行时提供随机输出。例如:“12345678910110”即使没有“launch”,有时它也会按预期工作,有时它会在一些值已经发出后开始收集,因此total的值小于11。

launch
1
2
-> 1 : total: 1
3
-> 1 : total: 2
4
-> 1 : total: 3
5
-> 1 : total: 4
6
-> 1 : total: 5
7
-> 1 : total: 6
8
-> 1 : total: 7
9
-> 1 : total: 8
10
-> 1 : total: 9
11
-> 1 : total: 10
-> 1 : total: 11
11

同时,多次运行第二个测试用例并没有让我看到]total]大于0。但是我注意到GlobalScope代码在随机时间执行,所以我可以在runBlocking的输出之前或中间看到launch的输出,但是onEach的输出仍然不存在。
我决定记录collect调用本身

GlobalScope.launch {
    println("launch")
    emitter.onEach {
        total += it;
        println("-> $it : total: $total")
    }.collect {
        "collecting"
    }
}

注意到它根本没有被调用。因为GlobalScope.launch的代码被调用的时间并不重要,我打赌句柄的作用域很重要。onEach作为一个侦听器工作,因此在runBlocking中发射有时会在第一个测试用例中更新它。当collect在正确的时间被调用时,它会收集。
但是监听不能在另一个作用域中发生,因此在第二种情况下,您将总是收到一个空流。如果您在第二种情况下添加一个带有emitting something的GlobalScope.launch,它将由handler处理并收集。

相关问题