【问题标题】:Android kotlin coroutines async awaitAndroid kotlin 协程异步等待
【发布时间】:2022-01-18 15:15:00
【问题描述】:

在我的 Viewmodel 类中,我执行下一个代码:

 init {
    viewModelScope.launch(Dispatchers.IO) {
        val homepageItemsCall = async { getHomepageItems() }
        val carouselItemsCall = async { getCarouselItems() }
        
        homepageItemsCall.await()
        carouselItemsCall.await()

        checkFavoriteStatus(homeItemsTest)
        carouselItems.postValue(carouselItemsTest)

    }
}

这就是我的 homepageItemsCall 的样子:

 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))
        }
    )
}

我的期望是这样的:

  1. 我在 ViewmodelScope 上创建了一个协程,它将执行 init 块中的代码。
  2. 因为我使用的是异步等待,所以在我的 API 调用完成之前我的代码不会被执行。这意味着我的两个 API 调用都将进入成功/失败状态,之后,我的代码可以进入下一行:“checkFavoriteStatus(homeItemsTest)”。

但它不是那样工作的。即使我使用了异步等待,在我的 API 调用完成之前程序会转到 checkFavoriteStatus(homeItemsTest) 行。我认为 async await 挂起/阻塞正在执行异步代码的协程(在这种情况下,协程正在执行我的整个 init 块..?我有什么问题吗?

如果是,那么等待我的 API 调用完成然后使用协程转到下一个代码的最佳方法是什么?

编辑这些是getHomePage和getResult函数:

   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()
}

【问题讨论】:

  • restService.getHomepage() 还是.getResult() 挂起?如果它们不是 getHomepageItems 函数将立即返回,您可能不希望在这里。
  • @ArpitShukla 是的,它们都是挂起函数
  • @Kratos 哦,这很奇怪。通常,只有一个等待网络的操作,它要么是挂起,要么是基于回调。在您的情况下,代码中似乎有 3 个不同的地方发生等待。这个休息客户端是什么? getHomepage()getResult() 是库提供的还是你实现的?你能分享他们的源代码吗?
  • @broot 当然没问题。看看我加的。 GetResult 是一个扩展函数,我们添加它是为了更好地处理错误。 GetHomePage 是 ViewModel 中的标准函数。
  • 所以,首先,您可能提供了一个不同的、名称相似的函数:GetHomePage() 而不是restService.getHomepage()。其次,这段代码中有一个奇怪的习惯,将所有函数标记为suspend,即使它们并没有真正挂起并以launch() 启动每个函数。 launch() 使跟踪操作变得不可能/更加困难,请等待它完成并获得结果。

标签: android kotlin kotlin-coroutines


【解决方案1】:

如果你想在 API 调用完成后做一些事情,你可以这样做

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

【讨论】:

  • 我尝试了您的解决方案,但由于我的 checkFavoriteStatus 是挂起乐趣,因此必须从协程调用,因此必须在 invokeOnCompletion 中添加协程调用。但它的行为仍然相同
【解决方案2】:

尝试将函数getHomepageItemsgetCarouselItems变为suspend,像这样:

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))
        }
    )
}

因为async 创建了一个协程来运行代码,而await 将等待它并从中获取结果。但是在您的代码中,async 中的协程是创建另一个 await 不会等待的协程

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-21
    • 2021-10-27
    • 1970-01-01
    • 2020-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-19
    相关资源
    最近更新 更多