I would like to provide some feedback on the learning materials which are used to explain coroutines.
Here is a code example from the coroutine guide:
fun main(args: Array<String>) = runBlocking<Unit> {
val job = launch { // launch new coroutine and keep a reference to its Job
delay(1000L)
println("World!")
}
println("Hello,")
job.join() // wait until child coroutine completes
}
This code launches a coroutine, than waits for it to complete. I had a lot of trouble with this code when I learned coroutines, and now I understand why.
The (mental) problem is that it is not made clear where the actual blocking is happening. Does launch {...} blocks anything or not? If not, then why is it inside runBlocking {...} ?
The answer to that is this code is excessive. Here is a version which helped me to understand what is going on:
fun main(args: Array<String>) {
val job = launch { // launch new coroutine and keep a reference to its Job
delay(1000L)
println("World!")
}
println("Hello,")
runBlocking<Unit> {
job.join() // wait until child coroutine completes
}
}
Here we 1) launch a coroutine 2) wait for it. This was much clearer to me. Ideally I would even prefer it to be like job.joinBlocking(), so it looks like threads even more.
I understand that your version is more idiomatic, easier to work with, etc, but I think that new learners need that extra step to understand the concept.
I rewrote the corresponding section in the guide in develop branch based on your suggestions. Take a look, please: https://github.com/Kotlin/kotlinx.coroutines/blob/develop/coroutines-guide.md#bridging-blocking-and-non-blocking-worlds
Awesome, I think that is much clearer!
One point is still not very clear thou:
Why don't we use something like job.joinBlocking()? AFAIK we don't have such an API. Why not?
From a learner's point of view, coroutines are like threads, thus they should have conceptually similar API. It was one of the first questions I asked myself when reading the docs, IMHO it should be answered at least in short.
We don't have joinBlocking to avoid API pollution. It has to be sufficiently useful in order to be added. Moreover, using it in the sample code would defeat the purpose of explaining that any suspending invocation can be converted into blocking using runBlocking { ... } around it.
Moreover, it overly encourages blocking where in a normal application you
almost never block (except maybe in main).
On Wed, Nov 22, 2017, 10:19 AM Roman Elizarov notifications@github.com
wrote:
We don't have joinBlocking to avoid API pollution. It has to be
sufficiently useful in order to be added. Moreover, using it in the sample
code would defeat the purpose of explaining that any suspending invocation
can be converted into blocking using runBlocking { ... } around it.—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/Kotlin/kotlinx.coroutines/issues/166#issuecomment-346381279,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAEEEUrQwAlYC-nfHoP5dSQfQAicj675ks5s5DuCgaJpZM4QgiA0
.
I see.. The converting ANY suspension function part slipped away from me.
I hope you agree with me that those (absolutely valid) points are not obvious, since they go agains the principal of minimal effort. IMHO the docs will benefit from including those explanations.
_The result is the same, but this code uses only non-blocking delay. The the main thread, that invokes runBlocking, blocks until the coroutine inside runBlocking is active._
One small thing that bugged me..
I would say until the coroutine inside is started, runned and completed
Most helpful comment
I rewrote the corresponding section in the guide in
developbranch based on your suggestions. Take a look, please: https://github.com/Kotlin/kotlinx.coroutines/blob/develop/coroutines-guide.md#bridging-blocking-and-non-blocking-worlds