持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情
启动流
使用流表示来自一些源的异步事件是很简单的。 在这个案例中,我们需要一个类似 addEventListener 的函数,该函数注册一段响应的代码处理即将到来的事件,并继续进行进一步的处理。onEach操作符可以担任该角色。 然而,onEach 是一个过渡操作符。我们也需要一个末端操作符来收集流。 否则仅调用 onEach 是无效的。
如果我们在 onEach 之后使用 collect 末端操作符,那么后面的代码会一直等待直至流被收集:
// 模仿事件流
fun events(): Flow<Int> = (1..3).asFlow().onEach { delay(100) }
fun main() = runBlocking<Unit> {
events()
.onEach { event -> println("Event: $event") }
.collect() // <--- 等待流收集
println("Done")
}
使用launchIn替换collect我们可以在单独的协程中启动流的收集
fun main() = runBlocking<Unit> {
events()
.onEach { event -> println("Event: $event") }
.launchIn(this) // <--- 在单独的协程中执行流
println("Done")
}
launchIn 必要的参数 CoroutineScope指定了用哪一个协程来启动流的收集
在实际的应用中,作用域来自于一个寿命有限的实体。在该实体的寿命终止后,相应的作用域就会被取消,即取消相应流的收集。这种成对的 onEach { ... }.launchIn(scope) 工作方式就像 addEventListener 一样。而且,这不需要相应的 removeEventListener 函数, 因为取消与结构化并发可以达成这个目的。
launchIn 也会返回一个 Job,可以在不取消整个作用域的情况下仅取消相应的流收集或对其进行 join
流的取消
流采用与协程同样的协作取消。像往常一样,流的收集可以是当流在一个可取消的挂起函数中挂起的时候取消
fun simpleFlow3() = flow<Int>{
for (i in 1..3){
delay(1000)
println("emitting $i")
emit(i)
}
}
fun timeOutFun() = runBlocking{
//流在超时的情况下取消并停止执行
withTimeoutOrNull(2500){
simpleFlow3().collect {
value -> println(value)
}
}
}
流的取消检测
- 为流方便起见,流构建器对每个发射值执行附加的ensureActive检测以进行取消,这一位着从flow{...}发出的繁忙循环是可以取消的
- 出于性能原因,大多数其他流操作不会自行执行其他取消检测,在协程处于繁忙循环的情况下,必须明确检测是否取消
- 通过cancellable操作符来执行此操作
背压
- buffer(),并发运行流中发射元素的代码
- conflate(),合并发射项,不对每个值进行处理
- collectLatest(),取消并重新发射最后一个值
- 当必须更改CoroutineDispatcher时,flowOn操作符使用了相同的缓冲机制,但是buffer函数显式地请求缓冲而不改变执行上下文。
操作符
过渡流操作符
- 可以使用操作符转换流,就像使用集合与序列一样
- 过渡操作符应用于上游流,并返回下游流
- 这些操作符也是冷操作符,就像流一样。这类操作符本身不是挂起函数
- 它运行的速度很快,返回新的转换流的定义
末端流操作符
末端操作符是在流上用于启动流收集的挂起函数。collect是最基础的末端操作符,但是还有另外一些更方便使用的末端操作符
- 转化为各种集合,例如toList与toSet
- 获取第一个first值与确保流发射单个(single)值的操作符
- 使用reduce与fold将流规约到单个值
组合多个流
就像kotlin标准库中的sequence.zip扩展函数一样,流拥有一个zip操作符用于组合两个流中的相关值。
展平流
流表示异步接收的值序列,所以很容易遇到这样的情况,每个值都会触发对另一个值序列的请求,然而由于流具有异步的性质,因此需要不同的展平模式,为此存在一系列的流展平操作符
- flatMapConcat 连接模式
- flatMapMerge 合并模式
- flatMapLatest 最新展平模式
流的异常处理
当运算符中的发射器或者代码抛出异常时,有几种处理异常的方法
- try/catch块
- catch函数
流的完成
当流收集完成时(普通情况或异常情况),它可能需要执行一个动作
- 命令式finally块
- onCompletion声明式处理