kotlin 协程之异步流的展平、异常、完成与取消操作

301 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情

展平流

处理包含流的流,将其展平为单个单位进行下一步处理。

  • flatMapConcat:按顺序收集流,处理流
  • flatMapMerge:顺序调用代码块,并发收集结果流
  • flatMapLatest:展平模式的collectLatest
fun requestFlow(i:Int):Flow<String> = flow {
    emit("$i : First")
    delay(500)
    emit("$i : Second")
}
fun main() = runBlocking<Unit> {
    (1..3).asFlow().map { testFlow.requestFlow(it) }
        .collect { println("map:$it") }
    (1..3).asFlow().flatMapConcat { testFlow.requestFlow(it) }
        .collect { println("flatMapConcat:$it") }
    (1..3).asFlow().flatMapMerge { testFlow.requestFlow(it) }
        .collect { println("flatMapMerge:$it") }
}

map:kotlinx.coroutines.flow.SafeFlow@27ddd392

map:kotlinx.coroutines.flow.SafeFlow@19e1023e

map:kotlinx.coroutines.flow.SafeFlow@7cef4e59

flatMapConcat:1 : First

flatMapConcat:1 : Second

flatMapConcat:2 : First

flatMapConcat:2 : Second

flatMapConcat:3 : First

flatMapConcat:3 : Second

flatMapMerge:1 : First

flatMapMerge:2 : First

flatMapMerge:3 : First

flatMapMerge:1 : Second

flatMapMerge:2 : Second

flatMapMerge:3 : Second

flatMapLatest:1 : First

flatMapLatest:2 : First

flatMapLatest:3 : First

flatMapLatest:3 : Second

流异常

当流发射器带有异常时,流收集器可以使用try/catch来处理异常,捕获在发射器、过渡流操作符或末端操作符中发生的异常。捕获异常后收集器便会停止收集值。

 try {
  (1..5).asFlow().collect { value ->
                           println(value)
                           check(value < 3) {  //当value>=3抛出异常,停止收集
                             "Collected $value"
                           }
                          }
} catch (e: Throwable) {
  println("Caught $e")
}

1

2

3

Caught java.lang.IllegalStateException: Collected 3

流必须保证异常透明性。发射器可以使用catch操作符来保留异常的透明性,如果在构造器内使用try/catch处理异常,发射器的异常无法被收集器收集。同样,也是捕获异常后停止收集值。

catch仅捕获上游流异常,下游流的异常会逃逸。 处理方法:将collect操作符中的动作放到onEach操作符中,并将其放到catch操作符之前。这样就可以不使用try/catch捕获所有的异常。

(1..5).asFlow()
    .onEach { value ->
        check(value < 3) {  //当value>=3抛出异常
            "Collected $value"
        }
        println(value)
    }
    .catch { cause -> cause?.let { println(it) } } //对异常进行处理
    .collect()

1

2

java.lang.IllegalStateException: Collected 3

流完成

流可以通过命令式或声明式显示收集完成。

  1. 命令式:try/finally
try {
    (1..3).asFlow()
        .collect { println(it) }
}finally {
    println("Done")
}

1

2

3

Done

  1. 声明式:使用onCompletion操作符 onCompletion操作符可以接收一个可空的cause参数用于判断流收集是正常完成还是异常导致停止。但它只接收异常不处理异常,它能观察到所有异常,当上游流成功完成(没有取消或失败)的情况下接受一个null异常。onCompletion是最末端操作符,collection是它的上游流,collet没有捕获的异常会被onCompletion接收到。
(1..5).asFlow()
    .onCompletion { cause -> cause?.let { println("error:$it") } ?: let { println("Done") } }
    .collect {
        check(it < 3) { "Collect value" }
        println(it)
    }

1

2

error:java.lang.IllegalStateException: Collect value

流取消与检测

emit()发出的流可以通过cancel()直接取消,如果要取消asFlow()flowOf()的流,需要先添加cancellable()操作符进行转化,返回一个可取消的流。

flowOf(1,3,4,5,5)
    .cancellable()  //检测转换
    .collect {
        if (it==3) cancel()  //取消
        println(it) }