简体   繁体   English

如何取消/取消订阅协程流程

[英]How to cancel/unsubscribe from coroutines Flow

I notice a strange behavior when trying to prematurely cancel from a Flow.我在尝试过早地从 Flow 中取消时注意到一个奇怪的行为。 Take a look at the following example.看看下面的例子。

This is a simple flow that emits integer values这是一个发出整数值的简单流程

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

Then I call the createFlow function using this code然后我使用此代码调用createFlow函数

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

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

This is what is printed out这是打印出来的

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

Now I would expect that the flow should stop emitting integers once it reaches the value of 2 but instead it actually switches the isActive flag to false and keeps emitting without otherwise stopping.现在我希望流一旦达到 2 的值就应该停止发射整数,但实际上它会将 isActive 标志切换为 false 并继续发射而不停止。

When I add a delay between emissions the flow behaves as I would expect.当我在排放之间添加延迟时,流量的行为与我预期的一样。

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

This is what is printed out after calling the flow again (which is the expected behaviour).这是再次调用流程后打印出来的内容(这是预期的行为)。

0 isActive true
1 isActive true
2 isActive true

What can I do to cancel the flow emission exactly at the specified value without adding delay?如何在不增加延迟的情况下完全取消指定值的流量发射?

I came across a workaround in this related issue我在相关问题中遇到了一种解决方法

I have replaced every single collect with a safeCollect function in my project:我在我的项目safeCollect函数替换了每一个collect

/**
 * 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)
  }
}

I want to add that in1.3.7 version emissions from flow builder now check cancellation status and are properly cancellable.我想在1.3.7 版本中添加来自 Flow Builder 的排放,现在检查取消状态并且可以正确取消。 So the code in question will work as expected所以有问题的代码将按预期工作

I came up with this recently我最近想出了这个

it seems that it will only actually cancel if it reaches a suspending point and in your code that emits there is no such point似乎它只有在到达暂停点时才会真正取消,并且在您发出的代码中没有这样的点

to solve this either add yield() between emissions or some other suspending function like delay(100)要解决此问题,请在排放之间添加 yield() 或其他一些挂起函数,例如 delay(100)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM