https://kotlinlang.org logo
#coroutines
Title
# coroutines
s

Stephan Schroeder

07/22/2019, 3:17 PM
So I’m working through the Coroutine Basics https://kotlinlang.org/docs/reference/coroutines/basics.html#scope-builder and this section says: “_runBlocking and coroutineScope is that the latter does not block the current thread while waiting for all children to complete._” but if i add a coroutineScope block to the example code, it doesn’t change the output?!? In order to reproduce replace the code provided with:
Copy code
fun main() = runBlocking { // this: CoroutineScope
    coroutineScope {
        launch {
            delay(200L)
            println("2Task from runBlocking")
        }

        coroutineScope { // Creates a coroutine scope
            launch {
                delay(500L)
                println("3Task from nested launch")
            }

            delay(100L)
            println("1Task from coroutine scope") // This line will be printed before the nested launch
        }

        println("4Coroutine scope is over") // This line is not printed until the nested launch completes
    }
}
w

withoutclass

07/22/2019, 3:19 PM
I believe here, your
runBlocking
is waiting for it's children to complete.
coroutineScope
is a child of your
runBlocking
g

gildor

07/22/2019, 3:20 PM
What do you expect to see?
This example works exactly as it should
Exactly, this is the whole point of runBlocking: provide coroutines builder that allows to launch any coroutines inside and block the thread where it was called
s

Stephan Schroeder

07/22/2019, 3:21 PM
yes, but then the paragraph fails to bring across the difference between
runBlocking
and
coroutineScope
. If there is a difference between the two (and I’m sure there is), why doesn’t the output change??
w

withoutclass

07/22/2019, 3:23 PM
Sure it does, but you've composed a
coroutineScope
inside a
runBlocking
, which means that you're going to block while your
runBlocking
scope waits for it's child
coroutineScope
to complete.
g

gildor

07/22/2019, 3:23 PM
This part of documentation just said that runBlocking and coroutinScope has similar semantics: it "blocks" caller until all child coroutines are running, but runBlocking actually blocks thread (but it can be called from non-blocking code, it's like an adapter between blocking and coroutinws), coroutinScope just suspend, doesn't block thread, but it's suspend function, you can call it only from coroutine
👍 1
m

Marko Mitic

07/22/2019, 3:24 PM
from what I understand,
coroutineScope
inherits `runBlocking`s context, so it executes everything in single thread
👍 1
g

gildor

07/22/2019, 3:25 PM
coroutineScope inherits context of outer scope, but it has nothing to do with this behavior, you can set any other dispatcher result will be the same It behaves how it behaves because it's desired behavior, just combined in one snippet
👍 1
coroutineScope creates scope and suspend until all child coroutines are finished, if you run it from runBlocking it does the same, but runBlocking is designed to block the thread until all child coroutines are finished (not only coroutineScope which is supposed functiin, even until launch or async), if it wouldn't block the thread it would be useless for intended use case
s

Stephan Schroeder

07/22/2019, 3:28 PM
it’s hard to follow what you’re saying because I failed to understand how things are supposed to work from the example. I was very surprised that “Coroutine scope is over” isn’t printed first. If
coroutineScope
isn’t blocking the main thread, why isn’t
println("4Coroutine scope is over")
executed immediately?
g

gildor

07/22/2019, 3:31 PM
coroutineScope suspending, it doesn't return until inside block (and all child coroutines started from this block) are finished
s

Stephan Schroeder

07/22/2019, 3:31 PM
@gildor “coroutineScope creates scope and suspend until all child coroutines are finished, if you run it from runBlocking it does the same, but runBlocking is designed to block the thread ” this sounds like the exact same behaviour, I’m sure it’s not, but I fail to grasp the difference.
g

gildor

07/22/2019, 3:32 PM
coroutineScope itself doesn't block any threads
s

Stephan Schroeder

07/22/2019, 3:32 PM
it just suspends, which sounds a loot like being blocked.
g

gildor

07/22/2019, 3:32 PM
But because runBlocking must wait for all child coroutines to return result, it blocks calling thread, even if coroutinss inside block nothing
Do you understand concept of suspending? It's the main idea of coroutines, instead block thread, suspend invocation until result is ready
s

Stephan Schroeder

07/22/2019, 3:34 PM
i understand Dart’s async-await.
And javascript’s event loop
g

gildor

07/22/2019, 3:34 PM
Suspend function is like await(), but without await(),
s

Stephan Schroeder

07/22/2019, 3:35 PM
suspend
is definetly the new thing.
g

gildor

07/22/2019, 3:35 PM
You do not block main thread in JS when call await() on promise, right?
s

Stephan Schroeder

07/22/2019, 3:35 PM
no, i add an callback
g

gildor

07/22/2019, 3:35 PM
What do you mean?
s

Stephan Schroeder

07/22/2019, 3:35 PM
javascript is single threaded, so you can’t block this thread.
g

gildor

07/22/2019, 3:36 PM
Exactly
await() is syntactic sugar over callbacks
s

Stephan Schroeder

07/22/2019, 3:36 PM
that part I understand.
but there are no scopes 😉 in the async-await model
so that’s new here.
g

gildor

07/22/2019, 3:38 PM
Scope has nothing to do with suspend itself
It's abstraction for coroutines lifecycle
I would really recommend first read kotlinx.coroutines guide
s

Stephan Schroeder

07/22/2019, 3:39 PM
isn’t that what the link I started out with is??
g

gildor

07/22/2019, 3:39 PM
And about Scopes read this introduction blog post by Roman https://medium.com/@elizarov/structured-concurrency-722d765aa952
s

Stephan Schroeder

07/22/2019, 3:40 PM
I’m literally reading the Coroutines Basics section on Kotlin lang.
g

gildor

07/22/2019, 3:40 PM
Yes, you do, but documentations are not perfect, try to read a bit more, maybe it would be more clear :)
s

Stephan Schroeder

07/22/2019, 3:40 PM
will do 👍 Thanks
g

gildor

07/22/2019, 3:43 PM
This part of guide just tries to show basic coroutine builders, but it also explains about suspend, Scopes and how suspend different from async/await (which also exist, but as library function, not keywords)
Also I recommend to watch this talk by Roman, it shows some basics and rationale:

https://youtu.be/_hfBv0a09Jc

It doesn't mention structured concurrency (it didn't exist at the moment), but for structured concurrency check the blog post which I shared above
s

Stephan Schroeder

07/22/2019, 3:51 PM
I’ve watched the video some weeks ago. I’m studying the blog post.
@gildor So the Structured Concurrency blog post didn’t help at all (nothing new to be found there), but rereading your comment “_This part of documentation just said that runBlocking and coroutinScope has similar semantics…_” did (with the runBlocking as adapter between synchronous and asynchronous code comparisson). So basically runBlocking is a normal function while coroutineScope is a suspend function but both create a new CoroutineScope that treats the lambda provided to the function in the same way!?
g

gildor

07/23/2019, 12:47 PM
Structured Concurrency blog post didn’t help
You said that “but there are no scopes in the async-await model”, so I just pointed you to description of this approach and why it’s here to avoid confsion
So basically runBlocking is a normal function while coroutineScope is a suspend function but both create a new CoroutineScope
That’s correct
treats the lambda provided to the function in the same way
Yeah, I would say not lambda, but they provide coroutine scope and scope works the same for all builders, not only for those two, but builders with other semantics and usecases (like withContext, launch etc), it provides scope where you can run child coroutines (even background ones) without worry about lifecycle and leaks of them
👍 1
s

Stephan Schroeder

07/23/2019, 1:33 PM
I would say not lambda,
pretty sure it’s a lambda with (CoroutineScope) receiver. Hmm, so i checked the API for coroutineScope
Copy code
suspend fun <R> coroutineScope(
    block: suspend CoroutineScope.() -> R
): R (source)
without the
suspend
, block would be a lambda with receiver. I’m not sure if the name changes because of the
suspend
(not the first suspend, the second after block). Maybe to “suspending lambda with receiver”?!? But now we’re down to how to name things. I feels like the original issue has been resolved 🙂
g

gildor

07/23/2019, 3:29 PM
Yes, of course it's lambda, I mean that it's all about coroutine scope, lambda with receiver is just an implementation detail
Yes, it's suspending lambda with receiver,
2 Views