If I do: ```val t = scope.async { ... } val u = sc...
# coroutines
b
If I do:
Copy code
val t = scope.async { ... }
val u = scope.async { ... }
try {
    select<Unit> {
        t.onAwait { ... }
        u.onAwait { ... }
    }
} catch (c: CancellationException) {
    ...
} catch (t: Throwable) {
   ...
}
where
scope
is not cancelled anywhere and one of the
t
,
u
tasks throws an exception, should I always be able to catch the exception that was thrown in my block? I'm seeing that most of the time I get the exception thrown by
t
or
u
, but sometimes I get a
CancellationException
wrapping the exception thrown by
t
or
u
and I'm trying to figure out if this is to be expected when using
select
or if I'm doing something wrong.
b
Most likely you use Job in your
scope
that means if any of your async blocks fails, another will be cancelled as well. If you will switch to SupervisorJob another async won't be cancelled
b
In this case I'm only using it for these 2 tasks. I made up a little example here: https://pl.kotl.in/h0f2ojO-z
And I'm fine with everything being cancelled--that's what I want--but I want to get the real exception if a task throws, not
CancellationException
b
If you switch to
private val scope = CoroutineScope(SupervisorJob()+CoroutineName("FileRecordingService"))
you will catch only error from t
b
And just cancel the other task manually? I do want everything to cancel if anything throws, which is why I didn't use SupervisorJob.
Is it some kind of race, basically?
b
Anyway you have to cancel it, if T ends with no exception.
b
It will be cancelled automatically if the other one throws though (without supervisor scope)
Oh, sorry, I misunderstood what you said. Yes that's true but doesn't apply in my case: these tasks always continue to run until cancelled from higher up or throwing an exception.
b
then you can simply use
Copy code
try {
  coroutineScope {
    async { // t code }
    async { // u code }
  }
} catch (e: ...) {
}
it should work as you expected
b
Great, I'll give that a try. Thanks!
b
b
That code looks the same as the one I pasted, might have to generate a new link to see your changes?
b
open it in incognito window or force reload
playground aggressively uses the cache
b
Cool, got it.
b
but anyway regenerated https://pl.kotl.in/zL5IA3N1V
b
Is it possible to get the
coroutineScope
behavior (wait for all children to finish) within the scope of an explicit
CoroutineScope
? (e.g. the
scope
member of that class)
b
pull the job from this scope (scope.coroutineContext.job on latest version or scope.coroutineContext[Job] on older) and call join() on it
b
Pull the job from the
coroutineScope
scope and join it in the
scope
scope?
b
just call scope.coroutineContext.job.join() from any suspend fun
b
I ended up doing this, maybe there's a better way:
Copy code
scope.launch {
    try {
        coroutineScope {
           launch { ... }
           launch { ... }
        }
    } catch (...) {
    }
}.join()
I wanted it to be run as a child of
scope
so that I can have a
stop
method in the class which can call
cancel
on
scope
I found it did work out better to let the caller manage the job by launching it on its own, so no need for the scope member variable. Thanks a lot for the help @bezrukov!
b
🤞 yep, this is an idiomatic way to do this. That means you typically have to write regular suspend functions that use implicit coroutine context (propagated from the caller) instead of using CoroutineScope directly. Then caller can decide how/when to launch it and how/when to cancel it
b
👍