I'm reading the article about flows on kotlinlang.org: https://kotlinlang.org/docs/flow.html
They show the next example:
fun simple(): Flow<Int> = flow {
println("Flow started")
for (i in 1..3) {
delay(100)
emit(I)
}
}
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) }
}
And they say that the output is:
Calling simple function...
Calling collect...
Flow started
1
2
3
Calling collect again...
Flow started
1
2
3
Therefore, it seems like the UI thread is waiting for the first flow.collect
function to finish, before continuing to print "Calling collect again..."
I would expect that while the first flow builder runs, the system will print "Calling collect again...", so the output would be:
Calling simple function...
Calling collect...
Calling collect again...
Flow started
1
2
3
Flow started
1
2
3
What am I missing?
CodePudding user response:
collect
is a suspend
function. Suspend functions are synchronous in the calling code, so collect
is no different than calling forEach
on a List, as far as code execution order is concerned.
It's not blocking the calling thread, but it is suspending, which means the code in the coroutine waits for it to return before continuing. This is the primary feature of coroutines--that you can call time-consuming code synchronously without blocking the thread. Under the hood, the suspend function is doing something asynchronously, but to the coroutine that calls it, it is treated as synchronous.
Under the hood, the coroutine dispatcher is breaking up your coroutine into chunks that it passes to the calling thread to run. In between these chunks, where it is suspending, the calling thread is free to do other stuff, so it's not blocked.
My answer here might help with explaining the concept further.