Given a REST endpoint and two asynchronous coroutines each returning an integer, I want this endpoint to return their sum. The two functions (funA and funB) should run in parallel, in such a way that the whole computation should take ~3secs. I'm using SpringBoot 2.6.3 and Kotlin Coroutines 1.6.0. Here is my attempt:
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class Controllers {
@GetMapping(
value = ["/summing"]
)
fun summing(): String {
val finalResult = runBlocking {
val result = async { sum() }
println("Your result: ${result.await()}")
return@runBlocking result
}
println("Final Result: $finalResult")
return "$finalResult"
}
suspend fun funA(): Int {
delay(3000)
return 10
}
suspend fun funB(): Int {
delay(2000)
return 90
}
fun sum() = runBlocking {
val resultSum = async { funA().await() funB().await() }
return@runBlocking resultSum
}
}
The problem is that this code does not compile as await() is not recognised as a valid method. If I remove await() the two functions are executed in series (total time ~5 secs) and instead of the expected result (100), I get:
Your result: DeferredCoroutine{Completed}@1c1fe804
Final Result: DeferredCoroutine{Completed}@48a622de
and so the endpoint returns "DeferredCoroutine{Completed}@48a622de".
I want the endpoint to return "100" instead and within ~3secs. How can I achieve this?
CodePudding user response:
You really messed this up ;-) There are several problems with your code:
- Use
runBlocking()
only to use anotherrunBlocking()
inside it. - Use
async()
and immediately callawait()
on it (insumming()
) - it does nothing. funA().await()
andfunB().await()
don't make any sense really. These functions return integers, you can'tawait()
on already acquired integers.- Generally, using much more code than needed.
The solution is pretty simple: use runBlocking()
once to jump into coroutine world and then use async()
to start both functions concurrently to each other:
runBlocking {
val a = async { funA() }
val b = async { funB() }
a.await() b.await()
}
Or alternatively:
runBlocking {
listOf(
async { funA() },
async { funB() },
).awaitAll().sum()
}
Or (a little shorter, but I consider this less readable):
runBlocking {
val a = async { funA() }
funB() a.await()
}
Also,runBlocking()
is not ideal. I believe Spring has support for coroutines, so it would be better to make summing()
function suspend
and use coroutineScope()
instead of runBlocking()
- this way the code won't block any threads.