I don't have a concrete reason, just exploring pos...
# coroutines
f
I don't have a concrete reason, just exploring possibilities. However, an abstract example would be: if I want to explore some parallelism by launching children coroutines inside a coroutine, the immediate way seems to be by using a
coroutineScope{ launch {...} launch {...}}
. However,
coroutineScope
suspends until all the children coroutines are completed. Probably most of the times this is a good thing, however there may be situations where I want to continue without waiting for the child coroutines to end.
m
Not quite sure I get your problem, but here goes anyway. You could launch coroutines on an outer scope, by doing something like this:
Copy code
launch {
    val scope = this

    launch {
        val job = scope.launch {
            // Do something
        }
    } // Will not wait for "job" here.

} // It will wait for "job" here.
f
Thanks for the reply. I don't want to bypass any scope. I just want to launch a new coroutine using the current scope, inside a
suspend
function, without creating a new scope. A way would be to explicitly pass the
CoroutineScope
explicitly into the
suspend
function, however that seems odd.
I can access the coroutine context on any
suspend
(via the
coroutineContext
intrinsic), however the coroutine scope doesn't seem to be available that way.
g
A way would be to explicitly pass the
CoroutineScope
explicitly into the
suspend
function, however that seems odd.
It's not odd and recommended way to write such functions as extensions of CoroutineScope
f
I see. However, doesn't that imply that all the
suspend
functions in the path until the inner launch now need to be CoroutineScope extensions, so that this scope is explicitly passed?
g
You need CoroutineScope only if you launch new coroutines using launch/async, if you do so you must control lifecycle of this coroutine, so you need coroutineScope otherwise this coroutine will leak and will not be cancelled if parent scope is cancelled. And this (“suspends until all the children coroutines are completed”) make a lot of sense, because suspend functions should do something and return and do not left after call some non-manageable asynchronous operation But not all suspend functions run new coroutines and usually you need this just to do some operations in parallel which is possible now using
coroutineScope
builder, but still completely safe
however there may be situations where I want to continue without waiting for the child coroutines to end
If you just want to run a new global coroutine just use
GlobalScope
, but this is explicit now because you say “yes, this coroutine doesn’t have automatic lifecycle management and bindings to parent lifecycle”, but this should be pretty rare case (in most cases you should and can avoid it), but it still possible and will work the same way as coroutines worked before structured concurrency If you have some real life example, please do not hesitate to share it
u
I have the feeling that people assume that launching coroutines in the
GlobalScope
is by definition bad. I don't see it that way. For example in our app there are many instances where you start a coroutine as a reaction to a user's command (e.g. "play this music") which you really don't want to automatically cancel when the current scope (e.g. a presenter) is left, so you start them using
GlobalScope.launch()
. Indeed, I don't have a single invocation of
coroutineScope
in our whole project, because it's somehow never really what I need.
g
This is highly depends on your use cases If you want to do something in parallel
coroutineScope
is very helpful
u
You mean parallel, but somehow interdependent so the automatic cancellation cancels everything? Because if you want to provide independent parallelism (e.g. a bunch of different presenters using one service in parallel) that's again not fitting. But yeah, very use-case dependent. I just never seem to have these use-cases and so I started to get nervous because this new fancy structured coroutines stuff doesn't really apply to my project somehow 😄
g
easisiest case for that: you have a function that does something sending multiple requests and combine them to get result
But if “play this music” is instant and you don’t care about lifecycle, why do you need coroutine for that? Why not just use
fun playMusic(): Unit
?
u
Because the service offers that as a suspending function with a result which you might or might not care about. The actual code is as always a bit more complicated 😉
g
You can provide non-suspend API for that, looks more idiomatic for me