Why do I have to use `coroutineScope { }` only in...
# coroutines
v
Why do I have to use
coroutineScope {  }
only inside another coroutine or a suspending function? What's wrong with
fun main() = coroutineScope { <do the majic> }
? Do I need
runBlocking{}
there? What's the meaning of
runBlocking
with scoped coroutines if scopes wait for their children?
v
@Vsevolod Tolstopyatov [JB] I have read those. That's where the question have came from
Could you elaborate on "the why" of this API?
v
those sections do not explain the “why”, do they?
The main difference between runBlocking and coroutineScope is that the latter does not block the current thread while waiting for all children to complete.
You can think of
coroutineScope
as about syntactic sugar for
Copy code
val job = launch(coroutineContext, parent = Job()) {
// Here is the body of currentScope
}
job.join()
job.throwExceptionIfPresent()
coroutineScope
is more or less suspending version of
runBlocking
, which provides dispatcher from current
coroutineContext
for all children
v
coroutineScope
is more or less suspending version of
runBlocking
,
In other words,
runBlocking
is like
coroutineScoped
with pre-selected dispatcher that always blocks, correct?
v
They have one key difference:
runBlocking
blocks until given coroutine completes
coroutineScope
suspends until given coroutine and all children of the scope complete, no matter in what dispatcher
I think we can improve documentation and/or guide about this distinction
v
Ok, I see the technical difference now. May I ask then, isn't "suspending" is a synonym to "blocking" in the coroutine world?
I suggest we rename
runBlocking
into
coroutineScope
(without a receiver)
Should I create an issue?
v
I don’t think it’s a good idea: 1)
coroutineScope
will not compile because of resolution ambiguity 2) We prefer to explicitly reflect the fact that builder blocks a thread 3) After this change it will be possible to accidentally misuse suspending and blocking fun 4)
runBlocking
still should provide a scope otherwise everyone will be forced to use
GlobalScope
where it’s not really necessary
v
2) We prefer to explicitly reflect the fact that builder blocks a thread
Any coroutine scope stops execution flow. How it maps to threads is an implementation detail desided by the dispatcher.
3) After this change it will be possible to accidentally misuse suspending and blocking fun
How so? If I create a coroutine scope, it will wait for its children.
4)
runBlocking
still should provide a scope otherwise everyone will be forced to use
GlobalScope
where it’s not really necessary
Not sure I follow here. GlobalScope should almost never be used. I don't see how unification of scope builders will change that
Do other implementations like Trio distinguish when one creates a scope inside the main thread or another coroutine?
v
How it maps to threads is an implementation detail
It’s not and it has nothing to do with dispatcher. Blocking and suspension are not synonyms. E.g. in
blockingFoo: String
and
asyncFoo: Future<String>
whether function is async or not is not an implementation detail. Same for blocking vs. suspending, the only difference is that compiler hides a complexity behind callbacks.
How so?
Because they have the same name 🙂
Not sure I follow here
If
runBlocking
lambda won’t have a receiver, then it would be impossible to write
runBlocking { launch { ... } }
because now scope is missing.
d
Good question and a good answer, I would say. Good to know.
v
@Vsevolod Tolstopyatov [JB] thanks for the comments! I'll think about them over night 🙂