anyone know how to get coroutine context from a su...
# coroutines
t
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
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
There is
coroutineContext
as a property available within any
suspend
function or lambda.
👍 2
t
@simon.vergauwen that is a function
and not sure which is less overhead
suspendCoroutineUninterceptedOrReturn
or
withContext
s
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
that is coroutineContext() implemetation
again which is less expensive coroutineContext() or withContext() ?
g
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
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
coroutineScope is a function of course, coroutineContext is a property (and actually intrinsic)
t
oh
so.. same question again, not what I should use, which is less overhead
coroutineScope{}
or
withContext{}
?
g
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
so.. the suspend function does not have scope?
g
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
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
Oh, no, it doesn't make sense and will not work
t
why?
g
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
so what should I do?
g
Could you please give your use case. Suspend function that returns Job is not a use case
t
my previous function does return a Job but that Job is already finish, am I correct?
g
Yes
t
lol
g
Do you want to use this job to cancel invocation of suspend function body?
t
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
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
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
To handle cancellation just wrap suspend function to try/catch/finally
t
doWork is called by the framework
g
In finally you can clean resources
So?
This is just suspend function, use try catch to handle cancellation
t
do you mean I need to try catch in doActualWork?
or in doWork?
g
anywhere, depends on where resource is acquired
If you want to keep current semantics, than in doWork
t
I thought when suspend function got canceled, it will stop immediately, no change to reach catch or final
g
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
note that
doWork
is called by that framework and canceled by the framework too, do we need doActualWork at all?
g
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
assume that I could do it in try{}, doWork got canceled, does the catch and final statement get called?
g
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
thank for your support
I appreciate it
g
You are welcome
d
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
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