1.介绍
我们在协程中处理的时候,往往面临函数的异步返回,如果异步返回多个值,我们该如何处理这些数据值之间的计算呢?
在这种情况下,Flow
流就是我们最佳解决方案了。所以,本篇主要学习Flow
的使用。
希望能够让大家了解Flow
是什么。
2.Flow 介绍
2.1 Flow 的初级使用
示例:
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
fun simple(): Flow<Int> = flow {
for (i in 1..3) {
delay(100)//阻塞100毫秒
emit(i) //发送数据值给流
}
}
fun main() = runBlocking<Unit> {
launch {
for (k in 1..3) {
println(" 我没有被锁定$k")
delay(100) //阻塞100毫秒
}
}
simple().collect { value -> println(value) }
}
//输出
我没有被锁定1
1
我没有被锁定2
2
我没有被锁定3
3
结论:
flow{...}
代码块中的代码可以被挂起。- 上面的函数
simple
不需要suspend
修饰符就可以使用。 Flow
使用emit
函数将结果值发送出去。简称:发射值。Flow
使用collect
函数收集发送的值。简称:收集值。
2.2 Flow 流是冷的
Flow
是一种类似序列的冷流,意思就是说流里面的代码并不是实时的,而是直到流被collect
函数进行收集的时候才会运行
所以说流是冷的。
2.3 Flow的取消基础
Flow
采用和协程一样的取消模式。流的数据的收集可以在当流在一个可取消的挂起函数(delay
)中被暂停挂起时。
可以进行取消操作。
示例:
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
fun simple(): Flow<Int> = flow {
for (i in 1..3) {
delay(100)
println("发射 $i")
emit(i)
}
}
fun main() = runBlocking<Unit> {
withTimeoutOrNull(250) { // 在 250 毫秒后超时
simple().collect { value -> println(value) }
}
println("结束")
}
//输出
发射 1
1
发射 2
2
结束
我们通过输出的结果就可以看到,Flow 流并没有能够输出到最后值3 ,而是在2之后就被关闭取消了。
2.4 Flow的构建器
我们在上面的Flow的初级使用中,flow{...}
就是Flow
的一个构建器,而且是一个最基础的构建器。
还有其他的几种构建器声明方式:
flow{...}
:基础构建器。发射值我们可以自定义。flowOf
:定义了一个发射固定值的流。asFlow()
:扩展方式,可以将各种集合和序列转换为流。
asFlow
示例:
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
fun main() = runBlocking<Unit> {
withTimeoutOrNull(250) { // 在 250 毫秒后超时
(1..3).asFlow().collect { value -> println(value) }
}
println("结束")
}
//输出
1
2
3
结束
3. Flow 操作符
在Flow
使用中,我们可以使用操作符转换流,就像使用集合与序列一样。
3.1 过渡流操作符
应用于上游流,并返回下游流。这个操作符本身不是挂起函数,所以它的运行速度比较快,返回新的转换流的定义。
操作符为 map
与filter
等。但是Flow
使用这些操作符合集合等序列对象的区别在于,可以调用挂起函数。
示例:一个请求中的Flow
可以使用map
操作符映射出结果,即使执行一个长时间的请求操作也可以使用挂起函数。
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
suspend fun performRequest(request: Int): String {
delay(1000) // 模仿长时间运行的异步工作
return "返回值: $request"
}
fun main() = runBlocking<Unit> {
(1..3).asFlow() // 一个请求流
.map { request -> performRequest(request) }
.collect { response -> println(response) }
}
//输出
返回值: 1
返回值: 2
返回值: 3
每隔一秒打印一个返回值。
3.1.1 转换操作符-transform
在Flow
转换操作符中最通用的一种是transform
。它可以用来模仿简单的转换,我们可以用它来发射任意值,任意次。
示例:在执行长时间运行的异步请求之前,发射一个字符串并跟踪响应。
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.transform
suspend fun performRequest(request: Int): String {
delay(1000) // 模仿长时间运行的异步工作
return "返回值: $request"
}
fun main() = runBlocking<Unit> {
(1..3).asFlow() // 一个请求流
.transform { request ->
emit("提出请求 $request")
emit(performRequest(request))
}
.collect { response -> println(response) }
}
//输出
提出请求 1
返回值: 1
提出请求 2
返回值: 2
提出请求 3
返回值: 3
3.1.2 限长操作符-take
通常使用限长操作符在Flow
触及相应限制的时候,将执行取消。
示例:
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun numbers(): Flow<Int> = flow {
try {
emit(1)
emit(2)
println("这一行将不会被执行")
emit(3)
} finally {
println("返回最后的Number值")
}
}
fun main() = runBlocking<Unit> {
numbers()
.take(2) // 只获取前两个
.collect { value -> println(value) }
}
//输出
1
2
返回最后的Number值
上面的例子中通过take
限制了获取长度。而超过该限制,会执行协程的取消。该模式下协程的取消操作是通过抛出异常来执行的,也就是我们上面例子中的try ... finally
3.2 末端流操作符
是指在Flow
上用于启动流收集的挂起函数,例如collect
就是最基础的末端操作符
还有其他的末端操作符例如:
toList
,toSet
转换各种数据集合。first
获取第一个值与确保Flow
发射单个值的操作符等。- 使用
reduce
和fold
将Flow
约定到单个值
示例:
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking<Unit> {
val sum = (1..5).asFlow()
.map { it * it } // 数字 1 至 5 的平方
.reduce { a, b -> a + b } // 求和(末端操作符)
println(sum)
}
//输出
55
关于Flow 的信息一篇介绍不完,我们下篇继续。
评论区