https://kotlinlang.org logo
Title
r

rednifre

08/09/2022, 1:53 PM
Are these two basically identical? What’s the difference?
fun suspendFoo(scope: CoroutineScope): Deferred<Int> =
        scope.async { 4 }
    
    suspend fun foo(): Int {
        delay(0)
        return 4
    }
s

Sam

08/09/2022, 1:55 PM
With an example this simple, there’s obviously not a whole lot of difference, but the contents of the
scope
could still affect how the
async
job gets dispatched for example
r

rednifre

08/09/2022, 1:57 PM
Hm, but I thought suspend functions would also have to be run in a scope, so as long as I run foo in the same scope as I would pass to suspendFoo, would they then be identical?
I might be asking about the syntax details of “suspend”, because I currently think of it like a code macro, that takes the second function into the first one. Or what’s going on there? As in, “suspend” is a language keyword, but the library decides what it means?
s

Sam

08/09/2022, 2:00 PM
It is kind of like a code macro, but it’s more like
fun suspendFoo(cont: Continuation<Int>) {
    cont.resumeWith(4)
}
The library decides how to implement
Continuation
(which is an interface)
Practically speaking, in your example, I think you’re right that there wouldn’t be much difference between the two functions. In real use cases, where you’re dealing with things like errors and cancellations and structured concurrency, there would start to be some differences in behaviour.
For example, imagine the function becomes more complex and can now throw an exception. If you write
runCatching { foo() }
then no errors can escape from that function, but if you write
runCatching { suspendFoo(scope).await() }
then it can and will still propagate exceptions to the scope.
r

rednifre

08/09/2022, 2:12 PM
I see, very helpful, thank you! I’ll look into the Continuation interface and resumeWith next.
s

Sam

08/09/2022, 2:12 PM
I guess you are also already aware that
async
will launch the job in the background whereas calling a suspend function runs in the foreground. Your example is probably simple enough that both versions will complete immediately anyway, though I don’t know if that’s guaranteed.
I found that a really nice way to understand how Kotlin’s suspend functions work under the hood was to look at the implementation of
sequence { ... }
— because it’s based on
suspend
functions and continuations but is unrelated to (and much simpler than) the kotlinx.coroutines library
r

rednifre

08/09/2022, 2:16 PM
Thanks, I’ll have a look and come back with more questions another day.
r

Rick Clephas

08/09/2022, 4:33 PM
runCatching { foo() }
runCatching { suspendFoo(scope).await() }
If I am not mistaken both of these wil be the same in terms of the returned result.
await
will throw the exception similar to how calling a regular suspend function would. The main difference would be in immediately waiting the result or not. When using the
Deferred
approach you can execute some other work before awaiting the result. E.g the following will only for 2 seconds (since both delays are running at the same time.
val deferredDelay = async { delay(2_000) }
delay(1_000)
deferredDelay.await()
While the following would result in a 3 second delay (since the second is only started after the first completes).
delay(2_000)
delay(1_000)
Note that
async
doesn’t guarantee immediate execute. Instead the job is dispatched. If you need immediate execution you can pass
CoroutineStart.UNDISPATCHED
to the
start
parameter: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/
s

Sam

08/09/2022, 4:40 PM
scope.async
will attempt to cancel its parent when it fails, same as
scope.launch
. It's caught me out a few times.
r

Rick Clephas

08/09/2022, 5:03 PM
Ah yes. When used with a regular
Job
that would indeed affect other jobs inside the scope (
SupervisorJob
can be used if that isn’t the desired behaviour). But if that is desired or not will likely depend on where the scope is coming from. E.g. in case of
coroutineScope
that would be perfectly valid: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html