so usually when you have to make different API calls and wait, you do something like this:
viewModelScope.launch {
withContext(dispatcherProvider.heavyTasks) {
val apiResponse1 = api.get1() //suspend function
val apiResponse2 = api.get2() //suspend function
if (apiResponse1.isSuccessful() && apiResponse2.isSuccessful() { .. }
}
}
but what happens if I've to do multiple concurrent same API Calls with different parameter:
viewModelScope.launch {
withContext(dispatcherProvider.heavyTasks) {
val multipleIds = listOf(1, 2, 3, 4, 5, ..)
val content = arrayListOf<CustomObj>()
multipleIds.forEach { id ->
val apiResponse1 = api.get1(id) //suspend function
if (apiResponse1.isSuccessful()) {
content.find { it.id == id }.enable = true
}
}
liveData.postValue(content)
}
}
Problem with second approach is that it will go through all ids of multipleIds
list and make async calls, but content
will be posted probably before that. How can I wait all the responses from for each loop to be finished and only then postValue
of the content to view?
The preferred way to ensure a couple of asynchronous tasks have completed, is using coroutineScope
. It will suspend until all child jobs, e.g. all calls to launch
or async
, have completed.
viewModelScope.launch {
withContext(dispatcherProvider.heavyTasks) {
val multipleIds = listOf(1, 2, 3, 4, 5, ..)
val content = arrayListOf<CustomObj>()
coroutineScope {
multipleIds.forEach { id ->
launch { // this will allow us to run multiple tasks in parallel
val apiResponse = api.get(id)
if (apiResponse.isSuccessful()) {
content.find { it.id == id }.enable = true
}
}
}
} // coroutineScope block will wait here until all child tasks are completed
liveData.postValue(content)
}
}
If you do not feel comfortable with this rather implicit approach, you can also use a more functional approach, mapping your ids to a list of Deferred
using async
and then await them all. This will also allow you to run all tasks in parallel but end up with a list of results in the correct order.
viewModelScope.launch {
withContext(dispatcherProvider.heavyTasks) {
val multipleIds = listOf(1, 2, 3, 4, 5, ..)
val content = arrayListOf<CustomObj>()
val runningTasks = multipleIds.map { id ->
async { // this will allow us to run multiple tasks in parallel
val apiResponse = api.get(id)
id to apiResponse // associate id and response for later
}
}
val responses = runningTasks.awaitAll()
responses.forEach { (id, response) ->
if (response.isSuccessful()) {
content.find { it.id == id }.enable = true
}
}
liveData.postValue(content)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With