So I’m working through the Coroutine Basics <https...
# coroutines
s
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
I believe here, your
runBlocking
is waiting for it's children to complete.
coroutineScope
is a child of your
runBlocking
g
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
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
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
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
from what I understand,
coroutineScope
inherits `runBlocking`s context, so it executes everything in single thread
👍 1
g
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
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
coroutineScope suspending, it doesn't return until inside block (and all child coroutines started from this block) are finished
s
@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
coroutineScope itself doesn't block any threads
s
it just suspends, which sounds a loot like being blocked.
g
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
i understand Dart’s async-await.
And javascript’s event loop
g
Suspend function is like await(), but without await(),
s
suspend
is definetly the new thing.
g
You do not block main thread in JS when call await() on promise, right?
s
no, i add an callback
g
What do you mean?
s
javascript is single threaded, so you can’t block this thread.
g
Exactly
await() is syntactic sugar over callbacks
s
that part I understand.
but there are no scopes 😉 in the async-await model
so that’s new here.
g
Scope has nothing to do with suspend itself
It's abstraction for coroutines lifecycle
I would really recommend first read kotlinx.coroutines guide
s
isn’t that what the link I started out with is??
g
And about Scopes read this introduction blog post by Roman https://medium.com/@elizarov/structured-concurrency-722d765aa952
s
I’m literally reading the Coroutines Basics section on Kotlin lang.
g
Yes, you do, but documentations are not perfect, try to read a bit more, maybe it would be more clear :)
s
will do 👍 Thanks
g
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
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
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
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
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,