如何取消/取消协程流程

时间:2020-01-10 11:11:16

标签: android kotlin-coroutines kotlinx.coroutines.flow

当我尝试过早从Flow中取消时,我注意到一个奇怪的行为。看下面的例子。

这是一个发出整数值的简单流程

  private fun createFlow() = flow {
        repeat(10000) {
            emit(it)
        }
    }

然后我使用此代码调用createFlow函数

  CoroutineScope(Dispatchers.Main).launch {
            createFlow().collect {

                Log.i("Main", "$it isActive $isActive")
                if (it == 2) {
                    cancel()
                }
            }
        }

这是打印出来的

0 isActive true
1 isActive true
2 isActive true
3 isActive false
4 isActive false
etc...etc

现在,我希望流一旦达到2的值就应该停止发射整数,但是实际上它将isActive标志切换为false并保持发射而不停止。

当我在发射之间添加延迟时,流量的行为与我预期的一样。

private fun createFlow() = flow {
    repeat(10000) {
        delay(500) //add a delay
        emit(it)
    }
}

这是在再次调用该流程(预期的行为)之后打印出来的内容。

0 isActive true
1 isActive true
2 isActive true

如何在不增加延迟的情况下精确地取消流量排放?

3 个答案:

答案 0 :(得分:8)

我遇到了与this相关的问题

的解决方法

我在项目中用collect函数替换了每个safeCollect

/**
 * Only proceed with the given action if the coroutine has not been cancelled.
 * Necessary because Flow.collect receives items even after coroutine was cancelled
 * https://github.com/Kotlin/kotlinx.coroutines/issues/1265
 */
suspend inline fun <T> Flow<T>.safeCollect(crossinline action: suspend (T) -> Unit) {
  collect {
    coroutineContext.ensureActive()
    action(it)
  }
}

答案 1 :(得分:1)

我想补充一点,现在1.3.7 version中来自流量生成器的排放检查取消状态,并且可以正确取消。因此,相关代码将按预期工作

答案 2 :(得分:0)

我最近想到了这个

似乎只有在达到挂起点时才会真正取消,并且在发出的代码中没有这种点

解决此问题的方法是在排放之间添加yield()或其他一些诸如delay(100)之类的暂停函数