Hi, im new to kotlin coroutines and after a look i...
# coroutines
g
Hi, im new to kotlin coroutines and after a look in the docs i have a question, in case i have spring boot app (actually whatever app it doesnt matter) and lets say i have a rest api, something like this:
Copy code
//@RestController
//@RequestMapping("${v1}/ping")
public class TestController {

    @GetMapping(value = ["", "/"])
    public suspend fun testAsync(): Deferred<ApiResponse> {
        coroutineScope {
            // add here launch, async etc   
        }
    }

}
For every api should i add a coroutineScope to achieve structured concurrency ?
Second question: If yes, then if i want to be able to pass this scope it is correct if create an interface like this:
Copy code
public interface TestApi {
    public val coroutineScope: CoroutineScope
}
and whenever some wants to use launch, async will have to go though this?
s
Spring has built in support for suspend functions, that's the preferred approach
☝️ 1
g
meaning is redundant to add coroutineScope right?
s
you should NOT be using launch/async unless you really need it (e.g. to start a coroutine in a different scope that's bigger than the request scope) instead have suspend functions calling suspend functions
g
ok, thnx a lot !
Another question, in case i need to launch/async deeper in the code, should i just insert coroutineScope directly and use the builders or it would be better use and interface like the above one and use that one? Sorry for the spam
j
If you're using
suspend
functions in your controller, you shouldn't be returning
Deferred
- suspend functions are expressed like regular functions returning regular values. The fact that they compile to something asynchronous is all encapsulated in the
suspend
keyword. So your example should be:
Copy code
@GetMapping(value = ["", "/"])
    public suspend fun testAsync(): ApiResponse {
        // do stuff and return the response directly
    }
Now, you definitely can start coroutines to do stuff concurrently inside the controller's methods or at a lower level in the hierarchy. In that case, you need to use
launch
or
async
and in order to do that you should indeed use
coroutineScope { ... }
. For example:
Copy code
@GetMapping(value = ["", "/"])
    public suspend fun testAsync(): ApiResponse = coroutineScope {
        val something = async { getSomethingSuspending() }
        val otherthing = async { getOtherThingSuspending() }
        
        // the last expression is the value returned by coroutineScope (no return keyword here)
        createApiResponseBasedOnBoth(something.await(), otherthing.await())
    }

    private fun createApiResponseBasedOnBoth(thing: Something, other: Otherthing): ApiResponse {
        return ApiResponse(x = thing.prop1, y = other)
    }
It's important here that
await()
is called after all `async`s are launched, this way they are run concurrently and the second one can progress while you await for the first one. You should NOT do:
Copy code
val something = async { getSomethingSuspending() }.await() // BAD
val otherthing = async { getOtherThingSuspending() }.await() // BAD
The above would be equivalent to just:
Copy code
val something = getSomethingSuspending()
val otherthing = getOtherThingSuspending()
Which makes one call after the other (this could be what you want, but then write it in this simple way and don't use
async
☝️ 1
👍 1
you should NOT be using launch/async unless you really need it (e.g. to start a coroutine in a different scope that's bigger than the request scope)
@stojan This is misleading. You can definitely start coroutines to do stuff concurrently even without wanting them to run in bigger scopes. This is the whole point of using
coroutineScope
, please see my reply above
👍 1
g
Really appreciate the pointers !! One more question if that is ok with u, in the case that i want for the rest flow of the controller to fire some coroutinescopes to do stuff, is it an antipattern to store the scope in a variable and pass this around to use this to use the coroutine builders? but there will be cases where probably someone will not use this at all so it will be a waste of resources and should let them just create coroutineScopes whenever they need it?
j
is it an antipattern to store the scope in a variable and pass this around
Yes, it's an anti-pattern because it's already done implicitly for you when you call suspend functions. If you want to use coroutines to call things concurrently like I showed above, make your function
suspend
and use
coroutineScope { .. }
(this is exactly what it is for). Then the caller of this function will have to be suspending as well, and this will bubble up to the controller. The coroutine context of the caller will be inherited by the lower level functions when doing so (so Spring's dispatcher and exception handlers etc will work fine). Note that if you don't need to run some particular thing concurrently, you can just call suspend functions like regular functions, you don't need to manually start coroutines with
async
or
launch
if the logic you want to express is sequential
g
Ok got it, really thanks again !!
k
Thanks for the info!