0

I need to execute 4 parallel requests. Here is my code:

suspend fun fetchAsyncData() = coroutineScope {
    val first = async { repository.taskFirst() }
    val second = async { repository.taskSecond() }
    val third = async { repository.taskThird() }
    val fourth = async { repository.taskFourth() }

    val firstResult = first.await()
    val secondResult = second.await()
    val thirdResult = third.await()
    val fourthResult = fourth.await()
    
}

The problem is that with this approach, requests are executed in parallel, but I can get answers at the same time. That is, if some of the requests will be executed for 45 seconds, and some for 3 seconds, then I will be able to process the results of my requests only after 45 seconds. My task is that as soon as the answer to the first request is received, pass it to the view model, so that it, in turn, can display this piece of data in a fragment. Further, as soon as another response to the request is received, transfer one more data, and so on.

How can this be done, please help me?

testivanivan
  • 967
  • 13
  • 36

4 Answers4

1

Maybe something like this could work for you:

fun getRepoFlow() = channelFlow {

        coroutineScope {
            launch { send(async { "1" }.await()) }
            launch { send(async { delay(1000L); "2" }.await()) }
            launch { send(async { "3" }.await()) }
        }

    }
1

If all the tasks return the same result then you can create a flow and send data to it asynchronously. Here's an example:

fun main() {
    // a flow object inside your viewModel
    val flow: MutableStateFlow<Data?> = MutableStateFlow(null)

    // observe the flow. e.g. For android in Activity's lifecycle
    CoroutineScope(Dispatchers.Unconfined).launch {
        flow.collect {
            println(it)
        }
    }

    // get data from viewModel
    CoroutineScope(Dispatchers.Unconfined).launch {
        launch {
            flow.value = task1()
        }

        launch {
            flow.value = task2()
        }
    }

    // app's lifespan. Not needed in practical applications.
    while (true) {}
}

suspend fun task1(): Data {
    delay(2000)
    return Data(1, Date())
}

suspend fun task2(): Data {
    delay(1000)
    return Data(2, Date())
}

data class Data(val d: Int, val time: Date)

But if the tasks return different data, then you can create multiple flow and send data when each task returns.

philoopher97
  • 772
  • 1
  • 6
  • 18
1

With this approach you can handle different type of response and failure case.

       sealed class ResponseState {
            data class Content<T>(val data: T) : ResponseState()
            // handle network and server error state
            //data class error(e : Throwable): ResponseState()

        }

        val flow: MutableStateFlow<ResponseState?> = MutableStateFlow(null)
        coroutineScope {
            launch {
                val res = repository.taskFirst() // return type a
                flow.value = ResponseState.Content<a>(res)
            }
            launch {
                val res = repository.taskSecond()// return type b
                flow.value = ResponseState.Content<b>(res)
            }
            launch {
                val res = repository.taskThird() // return type c
                flow.value = ResponseState.Content<c>(res)
            }
            launch {
                val res = repository.taskFourth() // return type d
                flow.value = ResponseState.Content<d>(res)
            }
        }
        // here is an example with flow, you can use live data as well
        CoroutineScope(Dispatchers.Main).launch {
            flow.collect {
                when (it) {
                    is ResponseState.Content<*> -> {
                        // check response type
                        when (it.data) {
                            is a -> {
                                //handle taskFirst response
                            }

                            is b -> {
                                //handle taskSecond response
                            }

                            is c -> {
                                //handle taskThird response
                            }

                            is d -> {
                                //handle taskFourth response
                            }

                            else -> {}
                        }
                    }
                }
            }
        }
0

you should use every single line code after async. example:

suspend fun fetchAsyncData() = coroutineScope { 

val first = async { repository.taskFirst() }
val firstResult = first.await()
//use firstResult here

val second = async { repository.taskSecond() }
val secondResult = second.await()
//use secondResult here


val third = async { repository.taskThird() }
val thirdResult = third.await()
//use thirdResult here

val fourth = async { repository.taskFourth() }
val fourthResult = fourth.await()
//use fourthResult here

}

and call fetchAsyncData() function:

lifecycleScope.launch {
        fetchAsyncData()
    }
hossein
  • 638
  • 1
  • 3
  • 14