【问题标题】:Emit after cancellation in Kotlin flow在 Kotlin 流程中取消后发出
【发布时间】:2022-02-08 23:57:34
【问题描述】:

我有以下代码的更复杂版本:

val testFlow = flow {
    try {
        // Continually emit some values
    } catch (e: CancellationException) {
        // Wrap up and emit finished state value
    }
}

当我收集这个 testFlow 时,我从来没有收到完成的状态值。 documented 是流构建器在发出值之前执行 ensureActive 检查。在此链接中还有一个使用 IntRange.asFlow 的示例,它在发出值之前不检查取消并显示这些值会继续被收集。

有没有办法让自定义 kotlin 流在取消协程后发出最后一个要收集的值?

这是一个Kotlin Playground example(感谢@Tenfour04),它显示了根据流程的构造方式,取消后发出的不同行为。我只是不知道如何获得一个能够在取消后发出的不是由IntRange.asFlow 构造的流。

【问题讨论】:

  • 如果存在 CancellationException,则不能发出,因为这意味着不再收集流。
  • 感谢您的评论。 IntRange.asFlow 例子表明协程取消后似乎有办法继续收集。你能详细说明我的误解在哪里吗?
  • 我在该页面上没有看到任何示例。有一些在取消后记录某些内容但不发出任何内容的示例。当协程被取消时,它将在第一次机会时停止所有执行(任何挂起函数调用),因此即使您的 Flow 上的 onCompletion 运算符中有代码,onCompletion 块也只会执行代码到它调用的第一个挂起函数。
  • 你看过我给出的操场例子了吗? IntRange testFlow 继续发出值 4 和 5,即使在收集值 3 时取消了协程。如果我理解正确,流取消是合作的,并且 IntRange 示例不会立即合作取消自身,即使它似乎仍在发出值(至少隐含地)。不好意思一再问。我似乎没有理解 IntRange 示例和使用 flow {} builder 的自定义流程之间的技术差异。
  • 好的,我看了看,我很困惑。 IntRange.asFlow() 似乎不配合取消,即使您查看实现,它使用 flow 构建器和 emit,如果您直接重新构建相同的代码,它确实 合作!见这里:pl.kotl.in/KvFli_h59

标签: kotlin cancellation kotlin-flow


【解决方案1】:

IntRange.asFlow 在内部使用 unsafeFlow,其定义为:

inline fun <T> unsafeFlow(crossinline block: suspend FlowCollector<T>.() -> Unit): Flow<T> {
    return object : Flow<T> {
        override suspend fun collect(collector: FlowCollector<T>) {
            collector.block()
        }
    }
}

使用 unsafeFlow,即使在取消的协程中也可以发出,即使这个解决方案是一个非常 hack,我想用一个不需要访问内部 Kotlin 协程 API 的更官方支持的版本来替换它。

注意:取消后发射仅在使用时有效

try {
    ...
} catch (e: CancellationException) {
    ...
}

并且不使用unsafeFlow.catch {}

【讨论】:

    猜你喜欢
    • 2021-01-04
    • 2021-09-30
    • 2021-12-24
    • 2018-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多