Android kotlin coroutines async await

Viewed 1183

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:

  1. I create a coroutine on ViewmodelScope that is going to execute code in init block.
  2. 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?

Edit these are getHomePage and getResult functions:

   suspend fun <T> ResultWrapper<T>.getResult(
    success: suspend (value: T) -> Unit,
    genericError: (suspend (code: Int?, message: String?) -> Unit)? = 
  null,
    networkError: (suspend () -> Unit)? = null,
    error: (suspend () -> Unit)? = null
    ) {

    when (this) {
        is ResultWrapper.Success -> {
            success(value)
        }
        is ResultWrapper.GenericError -> {
            genericError?.let { it(code, errorMessage) }
        }
        is ResultWrapper.NetworkError -> {
            networkError?.let { it() }
        }
    }

    if (this is ResultWrapper.NetworkError || this is ResultWrapper.GenericError)
        error?.let { it() }
  }

       suspend fun getHomepage() = safeApiCall(Dispatchers.IO) {
    apiClient.getHomepageElements()
}
2 Answers

If you want to do something after your API call is completed you can do something like this

init {
    viewModelScope.launch(Dispatchers.IO) {
      homepageItemsCall =  getHomepageItems()
      carouselItemsCall =  getCarouselItems() 
        
    }.invokeOnCompletion{
        checkFavoriteStatus(homeItemsTest)
        carouselItems.postValue(carouselItemsTest)
      }
  }

Try to make function getHomepageItems and getCarouselItems to suspend, like this:

private suspend fun getHomepageItems() {
    restService.getHomepage().getResult(
        success = { value ->
            homeItemsTest = value
        },
        genericError = { _, message ->
            error.postValue(message)
        },
        networkError = {
            error.postValue(TranslationManager.getString(TKeys.LOGIN_NETWORK_ERROR))
        }
    )
}

Because async was created a coroutine to run the code, and await will wait it and get result from it. But in your code, the coroutine in async is create anoher coroutine which await will not wait it

Related