In my Viewmodel class I do the next code:
init {
viewModelScope.launch(Dispatchers.IO) {
val homepageItemsCall = async { getHomepageItems() }
val carouselItemsCall = async { getCarouselItems() }
homepageItemsCall.await()
carouselItemsCall.await()
checkFavoriteStatus(homeItemsTest)
carouselItems.postValue(carouselItemsTest)
}
}
This is how my homepageItemsCall looks:
private fun getHomepageItems() = viewModelScope.launch(Dispatchers.IO) {
restService.getHomepage().getResult(
success = { value ->
homeItemsTest = value
},
genericError = { _, message ->
error.postValue(message)
},
networkError = {
error.postValue(TranslationManager.getString(TKeys.LOGIN_NETWORK_ERROR))
}
)
}
My expectation was like this:
- I create a coroutine on ViewmodelScope that is going to execute code in init block.
- since I am using async await, my code will not be executed until my API calls are done. This means both of my API calls will go to success/failure and after that, my code can go to the next line: "checkFavoriteStatus(homeItemsTest)".
But it doesn't work like that. Program goes to checkFavoriteStatus(homeItemsTest) line before my API calls are done even though I used async await. I thought that async await suspends/blocks coroutine that is executing async code (in this case, coroutine that is executing my whole init block..? Did I get something wrong?
And if yes, what is the best way to wait for my API calls to finish and then go to the next code by using coroutines?
CodePudding user response:
You are mostly correct about async-await part. The problem is in the getHomepageItems()
function. You launch()
everything asynchronously and therefore this function returns immediately. Also, getResult()
seems to be asynchronous as well.
Make this function suspend
and make all code inside synchronous. The resulting code could be something like below, but it may need additional tuning:
private suspend fun getHomepageItems() {
suspendCoroutine<Unit> { cont ->
restService.getHomepage().getResult(
success = { value ->
homeItemsTest = value
cont.resume(Unit)
},
genericError = { _, message ->
error.postValue(message)
cont.resume(Unit)
},
networkError = {
error.postValue(TranslationManager.getString(TKeys.LOGIN_NETWORK_ERROR))
cont.resume(Unit)
}
)
}
}
Depending on the rest client library you use, it could be much easier to do as many such libraries have support for coroutines and suspend functions already. In such case, they often provide a suspend API, so you don't have to convert callbacks to suspend by yourself. Consult the documentation of the library.
CodePudding user response:
It's duplication of Kotlin coroutines await for 2 or more different concurrent requests
You should use awaitAll