18

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

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

  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

如何在不增加延迟的情况下完全取消指定值的流量发射?

4

3 回答 3

13

我遇到了这个相关问题的解决方法

我已经用我的项目中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)
  }
}
于 2020-01-10T12:36:49.503 回答
8

我想在1.3.7 版本中添加流生成器的排放现在检查取消状态并且可以正确取消。所以有问题的代码将按预期工作

于 2020-12-03T07:38:32.830 回答
0

我最近想出了这个

似乎它只有在达到暂停点时才会真正取消,并且在您发出的代码中没有这样的点

为了解决这个问题,要么在发射之间添加 yield(),要么在延迟(100)等其他暂停函数之间添加

于 2020-11-30T22:21:20.447 回答