带有 catch 运算符的 Kotlin Flow 仍然可以完成
Kotlin Flow with catch operator still completes
我无法理解 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 " }, { error("error") }, { "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 会捕获一个异常并阻止它传播(除非你再次抛出它),但它不能后退一步(到地图的乐趣)并告诉它神奇地从下一个元素应该在哪里等等
如果你想要那个,你需要在 .map
中放一个普通的 try/catch
乐趣。所以它会是:
fun main() {
val list = listOf("Hello", "error", "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
.
我无法理解 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 " }, { error("error") }, { "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 会捕获一个异常并阻止它传播(除非你再次抛出它),但它不能后退一步(到地图的乐趣)并告诉它神奇地从下一个元素应该在哪里等等
如果你想要那个,你需要在 .map
中放一个普通的 try/catch
乐趣。所以它会是:
fun main() {
val list = listOf("Hello", "error", "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
.