Is it better to be using a coroutineScope when all...
# coroutines
d
Is it better to be using a coroutineScope when all there is is one operation there? To be truthful, I made my own
launchInScope
function to do the same thing... just wondering if that's really the intention. If so, why not just abstract away the whole Job concept and let the end user deal only with scopes?
l
@dave08 In case of
launch
, you already can have a `try`/`catch` block, and the scope in which
launch
is called can be a supervisor or have a
SupervisorJob()
, so you should never need something like
launchInScope
.
d
So why is async so different? I thought superviserscope works on them too? @louiscad
l
@dave08
async
is designed to cancel its scope so other work is cancelled. It is basically a fail fast technique. If you don't put it in a local
coroutineScope { … }
, it will cancel the upper scope, possibly without a way to recover. supervisor is to stop cancellation propagation, which is suitable for base scopes like the ones you would have in a UI component, at the root server code, etc…
d
So it cancels even a supervisor scope, if not in its own scope? Even with a try catch around it @louiscad?
l
@dave08 Try to understand this by running it and looking into it on
<http://play.kotl.in|play.kotl.in>
,
<http://try.kotl.in|try.kotl.in>
or locally on your machine:
Copy code
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*

suspend fun main() {
    coroutineScope {
        println(1)
        val channel = actor<Int> {
            consumeEach {
                try {
                    println("Attempting to do work for $it")
                    coroutineScope {
                        val stuff1 = async {
                            check(it != 2)
                            it
                        }
                        delay(100)
                        stuff1.await()
                    }
                    println("Work done for $it")
                } catch (e: IllegalStateException) {
                    println("Caught it!")
                }
            }
        }
        repeat(5) {
            channel.send(it)
        }
        launch {
            delay(1000)
            println(2)
        }
        println(3)
        println(4)
        delay(2000)
        channel.close()
    }
    println(5)
}
Run it and see the
IllegalStateException
being caught successfully. Then, remove the inner
coroutineScope { … }
and run again -> see it fail. Then, replace the outer
coroutineScope { … }
with
supervisorScope { … }
and run again -> see it fail, with limited propagation.
d
Thanks for the example! Will do, when I'll be by a computer 🙂. I think there was talk about simplifying the mental model of coroutines... this is a nice example of where a beginner (even intermediate...) wouldn't have a clue of what to expect...
@louiscad I tried the example, but I still don't understand. The try without the scope and the one with supervisorScope both crash at 2 and give the same results, why is that? And what do you mean by limited propagation?
l
@dave08 Doesn't the supervisorScope one let the other coroutines out of it run?
d
https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS4zLjAiLCJwbGF0Zm9ybSI6ImphdmEiLCJhcmdzIjoiIiwibm9uZU1hcmtlcnMiOnRydWUsInRoZW1lIjoiaWRlYSIsImZvbGRlZEJ1dHRvbiI6dHJ1ZSwicmVhZE9ubHkiOmZhbHNlLCJjb2RlIjoiaW1wb3J0IGtvdGxpbnguY29yb3V0aW5lcy4qXG5pbXBvcnQga290bGlueC5jb3JvdXRpbmVzLmNoYW5uZWxzLipcblxuc3VzcGVuZCBmdW4gbWFpbigpIHtcbiAgICBjb3JvdXRpbmVTY29wZSB7XG4gICAgICAgIHByaW50bG4oMSlcbiAgICAgICAgdmFsIGNoYW5uZWwgPSBhY3RvcjxJbnQ+IHtcbiAgICAgICAgICAgIGNvbnN1bWVFYWNoIHtcbiAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICBwcmludGxuKFwiQXR0ZW1wdGluZyB0byBkbyB3b3JrIGZvciAkaXRcIilcbi8vICAgICAgICAgICAgICAgICAgICAgc3VwZXJ2aXNvclNjb3BlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhbCBzdHVmZjEgPSBhc3luYyB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hlY2soaXQgIT0gMilcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpdFxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgZGVsYXkoMTAwKVxuICAgICAgICAgICAgICAgICAgICAgICAgc3R1ZmYxLmF3YWl0KClcbi8vICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBwcmludGxuKFwiV29yayBkb25lIGZvciAkaXRcIilcbiAgICAgICAgICAgICAgICB9IGNhdGNoIChlOiBJbGxlZ2FsU3RhdGVFeGNlcHRpb24pIHtcbiAgICAgICAgICAgICAgICAgICAgcHJpbnRsbihcIkNhdWdodCBpdCFcIilcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmVwZWF0KDUpIHtcbiAgICAgICAgICAgIGNoYW5uZWwuc2VuZChpdClcbiAgICAgICAgfVxuICAgICAgICBsYXVuY2gge1xuICAgICAgICAgICAgZGVsYXkoMTAwMClcbiAgICAgICAgICAgIHByaW50bG4oMilcbiAgICAgICAgfVxuICAgICAgICBwcmludGxuKDMpXG4gICAgICAgIHByaW50bG4oNClcbiAgICAgICAgZGVsYXkoMjAwMClcbiAgICAgICAgY2hhbm5lbC5jbG9zZSgpXG4gICAgfVxuICAgIHByaW50bG4oNSlcbn0ifQ==
l
What a link!
🤓 1
d
https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS4zLjAiLCJwbGF0Zm9ybSI6ImphdmEiLCJhcmdzIjoiIiwibm9uZU1hcmtlcnMiOnRydWUsInRoZW1lIjoiaWRlYSIsImZvbGRlZEJ1dHRvbiI6dHJ1ZSwicmVhZE9ubHkiOmZhbHNlLCJjb2RlIjoiaW1wb3J0IGtvdGxpbnguY29yb3V0aW5lcy4qXG5pbXBvcnQga290bGlueC5jb3JvdXRpbmVzLmNoYW5uZWxzLipcblxuc3VzcGVuZCBmdW4gbWFpbigpIHtcbiAgICBjb3JvdXRpbmVTY29wZSB7XG4gICAgICAgIHByaW50bG4oMSlcbiAgICAgICAgdmFsIGNoYW5uZWwgPSBhY3RvcjxJbnQ+IHtcbiAgICAgICAgICAgIGNvbnN1bWVFYWNoIHtcbiAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICBwcmludGxuKFwiQXR0ZW1wdGluZyB0byBkbyB3b3JrIGZvciAkaXRcIilcbiAgICAgICAgICAgICAgICAgICAgc3VwZXJ2aXNvclNjb3BlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhbCBzdHVmZjEgPSBhc3luYyB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hlY2soaXQgIT0gMilcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpdFxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgZGVsYXkoMTAwKVxuICAgICAgICAgICAgICAgICAgICAgICAgc3R1ZmYxLmF3YWl0KClcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBwcmludGxuKFwiV29yayBkb25lIGZvciAkaXRcIilcbiAgICAgICAgICAgICAgICB9IGNhdGNoIChlOiBJbGxlZ2FsU3RhdGVFeGNlcHRpb24pIHtcbiAgICAgICAgICAgICAgICAgICAgcHJpbnRsbihcIkNhdWdodCBpdCFcIilcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmVwZWF0KDUpIHtcbiAgICAgICAgICAgIGNoYW5uZWwuc2VuZChpdClcbiAgICAgICAgfVxuICAgICAgICBsYXVuY2gge1xuICAgICAgICAgICAgZGVsYXkoMTAwMClcbiAgICAgICAgICAgIHByaW50bG4oMilcbiAgICAgICAgfVxuICAgICAgICBwcmludGxuKDMpXG4gICAgICAgIHByaW50bG4oNClcbiAgICAgICAgZGVsYXkoMjAwMClcbiAgICAgICAgY2hhbm5lbC5jbG9zZSgpXG4gICAgfVxuICAgIHByaW50bG4oNSlcbn0ifQ==
The one without supervisor and the one with...
I guess they're encoding the code in the editor into the url?
l
@dave08 You misread what I told you initially
d
Oh, I see, the OUTER scope!
l
I said to use
supervisorScope
in place of the OUTER
coroutineScope
d
But why should it fail when the inner scope is replaced?
l
It doesn't fail when there's an inner scope
The inner scope makes cancellation caused by failure recoverable
d
I mean when the inner scope is a supervisorScope... I thought the only difference was in the automatic cancellation of the children...
l
The inner scope should not be a supervisor in this case
d
What is the difference? 🤔
In this case, I mean
Also, replacing the OUTER one, seems to give the same result...
l
The supervisor needs to ignore failure/cancellation of children, so it doesn't cancel other coroutines launched from it, unless it gets cancelled itself explicitly, and doesn't propagate cancellation or failure to its parent unless again, it is cancelled explicitly
Yes, it fails too with outer supervisor because the exception is uncaught, which causes the program to be stopped.
d
So, to see the difference, I would need to add a try/catch around the OUTER supervisorScope?
Because it seems like the same result as removing the INNER scope...?
l
I'm not sure. I think adding a coroutine exception handler would work though
d
I really don't see why the supervisorScope should crash because of a failing child scope... (I mean concept-wise... not in practice), is this maybe a bug in the implementation?
l
Regardless, it's always better to have a local scope when you deal with
async
👍🏼 1
d
And just be aware that it won't help by supervisors...
l
Yes. The only place where I currently use
SupervisorJob()
is in LifecycleOwners, and ViewModels (for Android apps) so a coroutine can be cancelled without preventing new ones from being launched afterwards
👍🏼 1