https://kotlinlang.org logo
#coroutines
Title
# coroutines
t

Tuan Kiet

06/02/2019, 8:47 AM
anyone know how to get coroutine context from a suspend function? in order to launch a job within a suspend function, we need to switch context with
withContext
to get
this
as a coroutine context?
t

tseisel

06/02/2019, 8:53 AM
To launch a new job, you need a
CoroutineScope
, not a
CoroutineContext
. The best way to obtain the CoroutineScope of a coroutine is to pass it as argument to the function that needs it.
👎 1
Also. There is a convention for defining functions that starts coroutines :
fun CoroutineScope.launchMyJob(...)
Functions defined like this does not suspend until the job is completed, as opposed to
suspend fun
.
s

simon.vergauwen

06/02/2019, 8:54 AM
There is
coroutineContext
as a property available within any
suspend
function or lambda.
👍 2
t

Tuan Kiet

06/02/2019, 9:00 AM
@simon.vergauwen that is a function
and not sure which is less overhead
suspendCoroutineUninterceptedOrReturn
or
withContext
s

simon.vergauwen

06/02/2019, 9:02 AM
I don't think
suspendCoroutineOrReturn
is what you're looking for. It's rather low level.
Do you just want to launch and forget? Or do you depend on the result?
t

Tuan Kiet

06/02/2019, 9:25 AM
that is coroutineContext() implemetation
again which is less expensive coroutineContext() or withContext() ?
g

gildor

06/02/2019, 9:49 AM
coroutineContext is not a function, this is a property
What is your use case? From your first message is completely not clear what you want to achieve, if you just want to use launch/async from suspend function use coroutineScope{} or supervisorScope{} functions which create scopes and suspend until child coroutineScope active, but you asking about coroutine context which cannot be used to start launch/async coroutines
withContext also works for this, but it make sense to use only if you really want to change context, otherwise just use coroutineScope/supervisorScope
t

Tuan Kiet

06/02/2019, 9:56 AM
wrong typing though, I mean
coroutineScope
pretty sure within a suspend function coroutineScope is a function
public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R
g

gildor

06/02/2019, 9:57 AM
coroutineScope is a function of course, coroutineContext is a property (and actually intrinsic)
t

Tuan Kiet

06/02/2019, 9:57 AM
oh
so.. same question again, not what I should use, which is less overhead
coroutineScope{}
or
withContext{}
?
g

gildor

06/02/2019, 9:59 AM
they are very similar, but if you don't want to switch context, only create a new scope, just use coroutineScope, not because of performance, bit just semantically
t

Tuan Kiet

06/02/2019, 10:01 AM
so.. the suspend function does not have scope?
g

gildor

06/02/2019, 10:02 AM
Probably coroutineScope a bit more performant just because less context objects will be created, but actually not sure, because coroutineScope also create own context, so I think it's the same, but use withContext just to create scope is just strange and confusing
Sure, there is no scope in suspend function and it's not needed for most of cases
t

Tuan Kiet

06/02/2019, 10:05 AM
apparently I need a function which will return a Job, which lead me to this, not sure it’s the best way?
Copy code
suspend fun sdfsdf(): Job {
    return coroutineScope {
        launch { 
            
        }
    }
}
g

gildor

06/02/2019, 10:05 AM
Oh, no, it doesn't make sense and will not work
t

Tuan Kiet

06/02/2019, 10:06 AM
why?
g

gildor

06/02/2019, 10:06 AM
Job will be always closed in this case, because coroutineScope will not return until launch is finished, this is whole point of this function
t

Tuan Kiet

06/02/2019, 10:07 AM
so what should I do?
g

gildor

06/02/2019, 10:07 AM
Could you please give your use case. Suspend function that returns Job is not a use case
t

Tuan Kiet

06/02/2019, 10:08 AM
my previous function does return a Job but that Job is already finish, am I correct?
g

gildor

06/02/2019, 10:08 AM
Yes
t

Tuan Kiet

06/02/2019, 10:08 AM
lol
g

gildor

06/02/2019, 10:09 AM
Do you want to use this job to cancel invocation of suspend function body?
t

Tuan Kiet

06/02/2019, 10:12 AM
Copy code
override suspend fun doWork(): Result = coroutineScope {
        workJob = launch {
            doActualWork()
        }
        workJob?.invokeOnCompletion {
            if (it != null) {
                showStoppedNotification()
            }
        }
        Result.success()
    }

    private suspend fun doActualWork() {
        // stuff
    }
doWork is a framework method
g

gildor

06/02/2019, 10:14 AM
If my assumption is correct, than no need to return Job from suspend function for this, just wrap to launch (or any other builder) on call site to control lifecycle, it's common idiom in coroutines, instead of putting launch inside, wrap it on call site, it make it universal (call site controls lifecycle) and safe to use (no way to leak it):
Copy code
suspend fun sdfsdf() {}

// On call site
val job = launch { sdfsdf() }
//Launch invocation in background and also may be cancelled
job.cancel()
t

Tuan Kiet

06/02/2019, 10:15 AM
and this can be cancel any time (by the framework) so I need a job instance to clean up stuff after it got canceled
and I don’t want to put everything I launch{} so I made a function for it
g

gildor

06/02/2019, 10:16 AM
To handle cancellation just wrap suspend function to try/catch/finally
t

Tuan Kiet

06/02/2019, 10:17 AM
doWork is called by the framework
g

gildor

06/02/2019, 10:17 AM
In finally you can clean resources
So?
This is just suspend function, use try catch to handle cancellation
t

Tuan Kiet

06/02/2019, 10:17 AM
do you mean I need to try catch in doActualWork?
or in doWork?
g

gildor

06/02/2019, 10:19 AM
anywhere, depends on where resource is acquired
If you want to keep current semantics, than in doWork
t

Tuan Kiet

06/02/2019, 10:21 AM
I thought when suspend function got canceled, it will stop immediately, no change to reach catch or final
g

gildor

06/02/2019, 10:21 AM
Copy code
override suspend fun doWork(): Result {
        try {
            doActualWork()
        } catch (e: CancellationException) {
            // You can handle cancellation separately, if you want
       } catch (e: Exception) {
            showStoppedNotification()
           //Should we return Result.fail()?
       }
       return Result.success()
    }

    private suspend fun doActualWork() {
        // stuff
    }
when coroutine where suspend function should be invoked is cancelled, suspend function will throw cancellation exception and this exception propagated to parent suspend functions
t

Tuan Kiet

06/02/2019, 10:23 AM
note that
doWork
is called by that framework and canceled by the framework too, do we need doActualWork at all?
g

gildor

06/02/2019, 10:24 AM
but you should put
//stuff
somewhere, isn’t it? I mean nothing prevents you from inline it in doWork, like with any other function, I’m not sure what is your concern in this case
t

Tuan Kiet

06/02/2019, 10:25 AM
assume that I could do it in try{}, doWork got canceled, does the catch and final statement get called?
g

gildor

06/02/2019, 10:29 AM
Of course, otherwise it would break error handling and resource handling (your case), suspend function cancellation based on throwing CancellationException that then handled differently by coroutines runtime, but try/catch/finally contract is still work for suspend functions
t

Tuan Kiet

06/02/2019, 10:35 AM
thank for your support
I appreciate it
g

gildor

06/02/2019, 10:38 AM
You are welcome
d

Dico

06/02/2019, 11:22 AM
Yes they get called, keep in mind that errors other than cancellation might also cancel parent coroutines regardless of catching the error in code.
Am I right Andrey?
g

gildor

06/02/2019, 12:12 PM
No, suspend function exception do not scope by itself (I mean handled exception, unhandled will be propagated and may cause scope cancellation or crash, the same as any other exception) if suspend function throws, it will not cancel scope automatically, so it's fine to try/catch it with no scope cancellation
Behavior with scope cancellation (if one of child background coroutines throws) designed to avoid coroutine resources leaking. Without this, if you would run 2 parallel jobs in one scope and one of them failed, it wouldn't cause cancellation of second one, so it will continue invocation, but usually result is useless, because another part of result is failed
8 Views