使用 buffer() 处理 Kotlin 流异常

时间:2021-04-09 10:44:46

标签: kotlin exception kotlin-coroutines kotlin-flow

我有一个在第二个元素上抛出异常的简单流程:

private val log = LoggerFactory.getLogger("test")

fun main() = runBlocking {
    flowOf(1, 2, 3)
        .onCompletion { log.info("Flow completed${if (it == null) "" else " exceptionally: ${it.message}"}") }
        .buffer()
        .map { throwingMapper(it) }
        .catch { log.error("Exception thrown") }
        .collect { log.info(it) }
    }
}

fun throwingMapper(it: Int): String {
    if (it == 2) {
        throw Exception("Test exception")
    }
    return "$it-mapped"
}

当我执行这段代码时,我得到以下输出 - 流程完成无一例外

2021-04-09 12:35:00.875 [main] INFO  - test:31 - Flow completed
2021-04-09 12:35:00.904 [main] INFO  - test:133 - 1-mapped
2021-04-09 12:35:00.915 [main] ERROR - test:34 - Exception thrown

但是,当我在 map 之前移动 buffer 运算符时:

flowOf(1, 2, 3)
        .onCompletion { log.info("Flow completed${if (it == null) "" else " exceptionally: ${it.message}"}") }
        .map { throwingMapper(it) }
        .buffer()
        .catch { log.error("Exception thrown") }
        .collect { log.info(it) }

产生以下输出并完成流程例外

2021-04-09 12:38:35.982 [main] INFO  - test:31 - Flow completed exceptionally: Test exception
2021-04-09 12:38:36.024 [main] ERROR - test:34 - Exception thrown

为什么在第一种情况下流程会无异常地完成? buffer 是否会默默吞下下游异常?或者它是否在内部创建了一个新的流程?如果是这样,是否有一些保留原始例外?

1 个答案:

答案 0 :(得分:1)

正如此方法的 documentation 所述:

<块引用>

<!DOCTYPE html> <html> <head> <title>Title</title> <meta charset="utf-8"> </head> <body> <figure class="figure" id="other figure"> <image src="https://p.bigstockphoto.com/GeFvQkBbSLaMdpKXF1Zv_bigstock-Aerial-View-Of-Blue-Lakes-And--227291596.jpg" width="100%" style="max-height: 70vh"></image> <figcaption>I am a figcaption.</figcaption> </figure> <float class="figure" id="my figure"> <image src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Image_created_with_a_mobile_phone.png/1200px-Image_created_with_a_mobile_phone.png" width="100%" style="max-height: 70vh"></image> <caption>I am a caption</caption> <figcaption>I am a figcaption 2.</figcaption> <p>1</p> <p>2</p> </float> <script> console.log(document.getElementsByTagName("float")); // Works fine with this custom tag console.log(document.getElementsByTagName("figure")); // Works fine console.log(document.getElementsByTagName("image")); // Fails, returns empty list with NON custom tag console.log(document.getElementsByTagName("caption")); // Fails, returns empty list console.log(document.getElementsByTagName("figcaption")); // Works fine console.log(document.getElementsByTagName("p")); // Works fine </script> </body> </html> 运算符在执行期间为其应用的流创建一个单独的协程。 [...] buffer 之前的代码将在一个单独的新协程 [...] 中与 [调用此代码的协程] 同时执行。

在第一种情况下,流程无异常完成,因为抛出异常的方法和捕获异常的方法都放在 buffer 的一侧,因此它们在同一个协程中执行。

<块引用>

协程之间使用通道将协程 P 发出的元素发送到协程 Q。

我认为不可能通过 buffer 发送异常,因此您可能需要在 Channel 之前和之后定义两个 catch 处理程序(如果您希望异常是两部分都抛出)。