Kotlin Flow document
A suspending function asynchronously returns a single value, but how can we return multiple asynchronously computed values? This is where Kotlin Flows come in.
Multiple values can be represented in Kotlin using collections
. For example, we can have a simple function that returns a List of three numbers and then print them all using forEach
:
fun main(args: Array<String>) {
simple().forEach { value -> println(value) }
}
fun simple() : List<Int> = listOf(1, 2, 3)
可以返回多个值但是不是异步的。
If we are computing the numbers with some CPU-consuming blocking code (each computation taking 100ms), then we can represent the numbers using a Sequence
fun main(args: Array<String>) {
simple().forEach { value -> println(value) }
println("----------------print over !------------")
}
fun simple() : Sequence<Int> = sequence { // sequence builder
for (i in 1..3) {
Thread.sleep(100) // pretend we are computing it
yield(i) // yield next value
}
}
1
2
3
----------------print over !------------
However, this computation blocks the main thread that is running the code.
When these values are computed by asynchronous code
we can mark the simple function with a suspend modifier
, so that it can perform its work without blocking and return the result as a list:
fun main(args: Array<String>) = runBlocking {
simple().forEach { value -> println(value) }
println("----------------print over !------------")
}
suspend fun simple() : List<Int> {
delay(1000) // do computation
return listOf(1,2,3)
}
这种方式是异步的,并且不会阻塞主线程,但是所有值是打包一起返回。Using the List
result type, means we can only return all the values at once.
为了表示异步计算的数据流,使用Flow
类型。
fun main(args: Array<String>) = runBlocking {
launch {
for (k in 1..3) {
println("I'm not blocked $k")
delay(100)
}
}
simple().collect {value ->
println(value)
} //这里挂起runBlocking
println("----------------print over !------------")
}
fun simple(): Flow<Int> = flow {
for (i in 1 .. 3) {
delay(100)
emit(i)
}
}
I'm not blocked 1
1
I'm not blocked 2
2
I'm not blocked 3
3
----------------print over !------------
This code waits 100ms before printing each number without blocking the main thread. This is verified by printing “I’m not blocked” every 100ms from a separate coroutine that is running in the main thread.
Flow代码和之前的代码有以下不同之处
Flow
类型的构造函数称为flow
.flow{...}
中的代码构建块可以挂起。simple()
函数不在是一个挂起函数emit
函数从流中发出值。collect
函数从流收集值。如果将
simple's flow { ... }
代码中的delay
替换为Thread.sleep
,主线程将阻塞
fun simple(): Flow<Int> = flow {
for (i in 1 .. 3) {
Thread.sleep(100)
emit(i)
}
}
1
2
3
----------------print over !------------
I'm not blocked 1
I'm not blocked 2
I'm not blocked 3
flow
传入的挂起函数
public fun <T> flow(@BuilderInference block: suspend FlowCollector<T>.() -> Unit): Flow<T> = SafeFlow(block)
·Flows
are cold streams similar to sequences — the code inside a flow builder does not run until the flow is collected
. This becomes clear in the following example:
fun main() = runBlocking<Unit> {
println("Calling simple function...")
val flow = simple()
println("Calling collect...")
flow.collect { value -> println(value) }
println("Calling collect again...")
flow.collect { value -> println(value) }
}
fun simple(): Flow<Int> = flow {
println("Flow started")
for (i in 1..3) {
delay(100)
emit(i)
}
}
Calling simple function...
Calling collect...
Flow started
1
2
3
Calling collect again...
Flow started
1
2
3
这是simple()
函数(which returns a flow)没有使用suspend修饰符标记的关键原因,simple()
调用本身返回很快,不等待任何东西。每次收集时,流都会重新启动,这就是为什么每次再次调用collect
时我们都会看到“flow started”
。
lows adhere to the general cooperative cancellation of coroutines. As usual, flow collection can be cancelled when the flow is suspended in a cancellable suspending function
(like delay). The following example shows how the flow gets cancelled on a timeout when running in a withTimeoutOrNull
block and stops executing its code.
fun simple(): Flow<Int> = flow {
for (i in 1..3) {
delay(100)
println("Emitting $i")
emit(i)
}
}
fun main(): Unit = runBlocking {
withTimeoutOrNull(250) {
simple().collect {value -> println(value) }
}
println("Done")
}
Emitting 1
1
Emitting 2
2
Done
withTimeoutOrNull
是一个挂起函数,当超时后被取消,里面运行的流也会被取消。
public suspend fun <T> withTimeoutOrNull(timeMillis: Long, block: suspend CoroutineScope.() -> T): T? {
if (timeMillis <= 0L) return null
var coroutine: TimeoutCoroutine<T?, T?>? = null
try {
return suspendCoroutineUninterceptedOrReturn { uCont ->
val timeoutCoroutine = TimeoutCoroutine(timeMillis, uCont)
coroutine = timeoutCoroutine
setupTimeout<T?, T?>(timeoutCoroutine, block)
}
} catch (e: TimeoutCancellationException) {
// Return null if it's our exception, otherwise propagate it upstream (e.g. in case of nested withTimeouts)
if (e.coroutine === coroutine) {
return null
}
throw e
}
}
流的构造器,常见的就是flow{ ... }
,另外还有:
For example:two builders as floow are same.
fun simple(): Flow
fun simple(): Flow