Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Hard to grasp coroutine explanation #166

Closed
voddan opened this issue Nov 16, 2017 · 6 comments
Closed

Hard to grasp coroutine explanation #166

voddan opened this issue Nov 16, 2017 · 6 comments

Comments

@voddan
Copy link

voddan commented Nov 16, 2017

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.

@elizarov
Copy link
Contributor

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

@voddan
Copy link
Author

voddan commented Nov 20, 2017

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.

@elizarov
Copy link
Contributor

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.

@JakeWharton
Copy link
Contributor

JakeWharton commented Nov 22, 2017 via email

@voddan
Copy link
Author

voddan commented Nov 22, 2017

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.

@efruttero
Copy link

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

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants