问题描述
我无法理解 catch
运算符在 kotlin Flow
中的工作原理。
Here is the catch documentation
问题:
- 为什么
catch
的存在不允许Flow
在遇到异常时继续而不是完成? - 放置
catch
运算符似乎会改变行为。为什么我不能将catch
运算符放在链的末尾以查看相同的结果?在我的示例中,它仅在我将其放置在onEach
之前才执行。
第一个示例,将 catch
放在 onEach
之前:
fun main() {
// Flow of lambdas that return a String (or throw an Exception)
flowOf<() -> String>({ "Hello " },{ error("error") },{ "World" })
// Map to the result of the invocation of the lambda
.map { it() }
// This line will emit the error String,but then the flow completes anyway.
// I would expect the flow to continue onto "World"
.catch { emit("[Exception caught] ") }
.onEach { println(it) }
.launchIn(GlobalScope)
}
实际结果:
Hello [Exception caught]
预期结果:
Hello [Exception caught] World
第二个示例,将 catch
放在 onEach
之后:
fun main() {
// Flow of lambdas that return a String (or throw an Exception)
flowOf<() -> String>({ "Hello " },{ "World" })
// Map to the result of the invocation of the lambda
.map { it() }
.onEach { println(it) }
// I would expect this catch to emit,but it never gets here.
.catch { emit("[Exception caught] ") }
.launchIn(GlobalScope)
}
实际结果:
Hello
预期结果:
Hello [Exception caught] World
或者,由于 onEach
发生在 catch
的发射之前,catch
的发射将被忽略?在这种情况下,预期的输出是这样的?:
Hello World
解决方法
对我来说,解释您在做什么的最简单方法是将其简化为同步代码。你基本上是这样做的:
fun main() {
val list = listOf("Hello","error","World")
try {
for (s in list) {
if (s == "error") error("this is the error message here")
println(s)
}
} catch (e: Exception) {
println("the exception message is: ${e.localizedMessage}")
}
}
输出:
Hello
the exception message is: this is the error message here
如您所见,异常被捕获,但无法阻止 for
循环的停止。与该异常停止 map
函数的方式相同。
Flow.catch 会捕获一个异常并阻止它传播(除非你再次抛出它),但它不能退后一步(到地图乐趣)并告诉它神奇地从下一个元素的位置重新开始本来是或等
如果需要,您需要在 try/catch
乐趣中放入一个普通的 .map
。所以应该是:
fun main() {
val list = listOf("Hello","World")
for (s in list) {
try {
if (s == "error") error("this is the error message here")
println(s)
} catch (e: Exception) {
println("the exception message is: ${e.localizedMessage}")
}
}
}
输出:
Hello
the exception message is: this is the error message here
World
通常使用 Flow.catch
的方法是捕获一个会阻止下一步的异常,例如:
//pseudo
flow
.map{/*code*/}
.filterNotNull()
.doSomethingRisky() //this can throw an exception
.catch {} //do something about it
.doSomethingElse()
在这种情况下,即使 doSomethingRisky
抛出异常,流程仍将到达 doSomethingElse
。这或多或少是 Flow.catch
的用法。