带有catch操作符的Kotlin流仍然完成

带有catch操作符的Kotlin流仍然完成,kotlin,kotlin-coroutines,kotlin-flow,Kotlin,Kotlin Coroutines,Kotlin Flow,我很难理解catch操作符如何在kotlin流中工作。 问题: 为什么catch的存在不允许流在遇到异常时继续,而不是完成 catch操作符的放置似乎会改变行为。为什么我不能将catch操作符放在链的末尾以查看相同的结果?在我的示例中,仅当我将它放在onEach之前时,它才会执行 第一个示例,将catch放在onEach之前: fun main() { // Flow of lambdas that return a String (or throw an Exception)

我很难理解
catch
操作符如何在kotlin
流中工作。

问题:

  • 为什么
    catch
    的存在不允许
    流在遇到异常时继续,而不是完成
  • 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)
    }
    
    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 capture]World

    或者,由于
    onEach
    发生在
    catch
    的发射之前,因此
    catch
    的发射将被忽略?在这种情况下,预期的输出为:
    helloworld

    对于我来说,解释你们在做什么最简单的方法就是将它简化为同步代码。你基本上是这样做的:

    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 fun)并告诉它从下一个元素所在的位置神奇地重新开始

    如果你想这样做,你需要在
    .map
    fun里面放一个普通的
    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
    。这或多或少是对
    流的使用。catch

    是和否。如果您有另外两个调用
    .map
    ,如果您没有该
    。catch
    ,它将永远不会到达后续的两个调用。通过
    .catch
    它将到达它们(但仅限于第一个异常之前可以处理的元素)。但要处理所有元素,您还需要在
    .map
    函数中放入一个正常的try/catch。谢谢您的回答,@Alex。在查看catch文档时,它们提供了一个示例:`flow{emitData()}.map{computeOne(it)}.catch{…}//捕获emitData和computeOne中的异常。map{computeTwo(it)}.collect{process(it)}//从进程和计算中抛出异常`因此,我的map{it()}那个危险的电话会不会被抓到,对吧?顺便说一下,很抱歉格式太糟糕了。第一次如此。。这里有问题:-)注释的格式不能很糟糕,但是“char”确实有助于突出显示内容。顺便说一句,你的问题很好,我会考虑它的高质量,用适当的例子,在你想要达到的目标上非常干净。我见过有2年账户的人情况更糟。(顺便说一句,这条评论和你最近的一条评论很有可能被删除,因为它们是离题的,你知道的)