Kotlin协程中使用挂起函数(Suspend函数)可以异步地返回单个计算结果,但是如果有多个计算结果希望通过协程的方式异步返回,这时可以使用Flows(基于kotlin v. 1.3.61)。
一般我们可以使用集合类存储多个值,例如foo()返回一个list,包含3各成员,可以通过foreach对其遍历并打印结果
fun foo(): List = listOf(1, 2, 3)
fun main() {
foo().forEach { value -> println(value) }
}
输出:
1
2
3
除了list以外,我们可以使用squences对单个数值计算后逐条返回,但是计算过程是同步的
fun foo(): Sequence = sequence { // sequence builder
for (i in 1..3) {
Thread.sleep(100) // pretend we are computing it
yield(i) // yield next value
}
}
fun main() {
foo().forEach { value -> println(value) }
}
打印结果相同,每100ms输出一个
上面的计算会阻塞UI线程,此时我们会考虑使用协程挂起的方式返回结果:
suspend fun foo(): List {
delay(1000) // pretend we are doing something asynchronous here
return listOf(1, 2, 3)
}
fun main() = runBlocking {
foo().forEach { value -> println(value) }
}
1s后结果被一次性输出
使用 List
fun foo(): Flow = flow { // flow builder
for (i in 1..3) {
delay(100) // pretend we are doing something useful here
emit(i) // emit next value
}
}
fun main() = runBlocking {
// Launch a concurrent coroutine to check if the main thread is blocked
launch {
for (k in 1..3) {
println("I'm not blocked $k")
delay(100)
}
}
// Collect the flow
foo().collect { value -> println(value) }
}
数值和“I'm not blocked”并行打印,说明collect并没有阻塞UI
I'm not blocked 1
1
I'm not blocked 2
2
I'm not blocked 3
3
通过上面例子可以看到,Flow有以下特征:
Flows像Sequences一样是冷流,即在调用collect之前,flow{ ... }中的代码不会执行
fun foo(): Flow = flow {
println("Flow started")
for (i in 1..3) {
delay(100)
emit(i)
}
}
fun main() = runBlocking {
println("Calling foo...")
val flow = foo()
println("Calling collect...")
flow.collect { value -> println(value) }
println("Calling collect again...")
flow.collect { value -> println(value) }
}
输出结果:
Calling foo...
Calling collect...
Flow started
1
2
3
Calling collect again...
Flow started
1
2
3
这也是 foo() 不需要标记为suspend的原因,因为foo()中的执行不需要挂起,可以很快返回结果,等到调用collect的时候才开始执行。
Flow创建后并不返回可以cancel的句柄,但是一个flow的collect是suspend的,所以可以像取消一个suspend方法一样取消flow的collection。就好像RxJava中没有subscribe之前仅仅创建一个Observable是不需要unsubscribe的
fun foo(): Flow = flow {
for (i in 1..3) {
delay(100)
println("Emitting $i")
emit(i)
}
}
fun main() = runBlocking {
withTimeoutOrNull(250) { // Timeout after 250ms
foo().collect { value -> println(value) }
}
println("Done")
}
输出结果:
Emitting 1
1
Emitting 2
2
Done
从前面的例子中我们知道可以通过 flow { ... } 创建Flow。除此之外还有以下两种方式
例如可以将一个IntRange转成一个flow
(1..3).asFlow().collect { value -> println(value) }