关于 Flow 的文档描述了为什么emit
永远不应该在 try/catch 块中运行:
实现从不捕获或处理下游流中发生的异常。从实现的角度来看,这意味着对
emit
和emitAll
的调用永远不应该被包装到try { ... } catch { ... }
块中。流中的异常处理应使用catch
运算符执行,它旨在仅捕获来自上游流的异常,同时传递所有下游异常...相同的推理可以应用于作为 finally 块的声明性替换的onCompletion
运算符。
但是,onCompletion
的文档指定
与
catch
不同,此运算符报告上游和下游发生的异常,并观察为取消流而引发的异常。当且仅当流已成功完成时,异常为空。
那么:如果对下游异常不透明,那么使用finally
和onCompletion
有什么区别? 在许多用例中,onCompletion
非常难以使用,特别是任何基于"资源尝试"的流,其中流中没有传递状态。
具体来说,我会对一段演示行为差异的代码感到满意。
使用两者后我的结论是:
- 当清理代码需要知道流是成功还是失败时,
onCompletion
很有用 - 当您始终希望运行完全相同的清理而不管流是成功还是失败时,
try
/finally
非常有用
这是因为onCompletion
接收失败原因作为参数,或者null
流是否正常完成,没有异常。
myFlow.onCompletion { failure ->
if (failure == null) println("Success") else println("Failure")
}
从文档中:
onCompletion的主要优点是lambda的可为空的
Throwable
参数,可用于确定流收集是正常完成还是异常完成。https://kotlinlang.org/docs/flow.html#declarative-handling
用try
/catch
实现同样的事情有点冗长(实际上需要一个catch
而不是finally
:
try {
collect()
} catch (t: Throwable) {
println("Failure")
throw t
}
println("Success")
你提到了尝试资源。我认为重要的是要注意流生成器中的try
/finally
与流收集器中的try
/finally
之间的区别。前者对于关闭流使用的资源很有用;后者更类似于onCompletion
.
上面的示例显示了流收集器中的try
/catch
。如果您不需要知道它是成功还是失败,它可以很容易地替换为try
/finally
。另一方面,在流生成器中使用finally
进行清理将如下所示:
flow {
val myResource = openResource()
try {
myResource.values.forEach { emit(it) }
} finally {
myResource.cleanup()
}
}
实际上,您可能会使用use
而不是try
/finally
来做到这一点。正如您所指出的,这不是您可以使用onCompletion
(或流收集器中的try
/catch
/finally
)执行的操作,因为它依赖于流构建器内部的状态。我认为流的一大好处是,流的每个阶段都可以以这种方式封装其设置和清理。
实际上,onCompletion
将上面的流收集器和流生成器示例组合在一起。这是因为在流无异常的情况下终止的情况下,onCompletion
可以发出新值。当流失败并出现异常时,它无法发出值这一事实是一种人为的限制,当您查看源代码时,实际上会使实现复杂化。
通过编写我们自己的流运算符,我们可以克服这个约束:
fun <T> Flow<T>.finally(block: suspend FlowCollector<T> -> Unit) {
try {
emitAll(this@finally)
} finally {
block(t)
}
}
使用上述运算符,someFlow.finally { emit(...) }
将始终发出一个新值,即使流失败并出现异常也是如此。我不确定它是否违反了禁止用try
/catch
包裹emit
的规定,但这感觉不是一个特别好的主意。
正如您所说,onCompletion
和finally
都将在上游异常、下游异常和正常完成的情况下运行。从这个意义上说,它们在行为上没有差异(据我所知),所以我无法举出一个例子来展示差异。
一个补充是onCompletion
的lambda可以访问一个FlowCollector
,该可以将更多的元素emit()
到下游流(当没有发生异常时)。
我想除此之外,这里唯一的区别是风格。就像.filter{}
一样 和if(condition) emit(it)
一样.