How consume multiple endpoints at same time using Kotlin Coroutine in a BackEnd Controller

Viewed 328

Context: I found few tutorials explaining how consume mutilple endpoints from Kotlin at same time but they are based on Android and in my case it is a backend application. I have some experience using CompleteableFuture but I assume I should use Coroutine since it is a Kotlin and there is no Spring dependency.

Following some suggestions, I reached

@Singleton
class PersonEndpoint()
    {

    @Inject
    lateinit var employeClient: EmployeClient

    override suspend fun getPersonDetails(request: PersonRequest): PersonResponse {
        var combinedResult: String

        GlobalScope.launch {
            val resultA: String
            val resultB: String

            val employeesA = async{ employeClient.getEmployeesA()}

            val employeesB = async{ employeClient.getEmployeesB()}

            try{

                combinedResult = employeesA.await() + employeesB.await()

                print(combinedResult)

            } catch (ex: Exception) {
                ex.printStackTrace()
            }

        // ISSUE 1
        if I try add return over here it is not allowed. 
        I understand it is working how it is designed to work: GlobalScope is running in different thread

        }
        
    // ISSUE 2
    if I try return combinedResult over here combinedResult isn't initialized.
    I understand it is working how it is designed to work: GlobalScope is running in different thread and I can
    debug and see that return over here executes earlier than employeesA.await = employeesB.await

}

So, how can I execute combinedResult = employeesA.await() + employeesB.await() before returning to the client?

*** Edited after Denis/ answer

@Singleton
class CustomerEndpoint(){

    fun serve(): Collection<Int> {
        return runBlocking {
            async {
                getItemDouble(1)
            }
            async {
                getItemTriple(1)
            }
        }.map { it.await() }
    }

suspend fun getItemDouble(i: Int): Int {
    delay(1000)
    return i * 2
}

suspend fun getItemTriple(i: Int): Int {
    delay(1000)
    return i * 3
}

override suspend fun getPersonDetails(request: PersonRequest): PersonResponse {

    val result = serve()
    println("Got result $result")
    
    ...
}
1 Answers
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlin.system.measureTimeMillis

fun main() {
    val durationMs = measureTimeMillis {
        val result = serve()
        println("Got result $result")
    }
    println("The processing is done in $durationMs ms")
}

fun serve(): Collection<Int> {
    return runBlocking {
        (1..2).map {
            async {
                getItem(it)
            }
        }.map { it.await() }
    }
}

suspend fun getItem(i: Int): Int {
    delay(1000) // Emulate item retrieval work
    return i * 2
}

Note that here we have two nested calls - getItem(1) and getItem(2). We can see that they are executed in parallel as overall running time is ~1 second.


Edited in August 05th 2021

private suspend fun myMethod(): List<Any> {
    return runBlocking {
        listOf(
            async { method1() },
            async { method2() }
        ).map { it.await() }
    }
}

method1 and method2 are methods calling different endpoints.

Related