关于Kotlin Coroutines执行的困惑:在哪里调用await()?

时间:2019-05-29 05:56:54

标签: android kotlin retrofit

我阅读了很多有关Kotlin协程的文档,但仍然有一些疑问。我正在使用带有协程的Retrofit,因此我需要对Dispatchers.IO上下文进行请求,但在Dispatchers.Main上下文中使用result将其分配给ViewModel。我的代码是:

fun doHttpreq() {
    viewModelScope.launch(Dispatchers.IO) {
        try {
            //should I call await() here? (I guess the correct way to keep execution of request outside of Main thread)
            val request = RestClient.instance.getItems().await()

            withContext(Dispatchers.Main) {
                //or should I call await() here? (BUT need request to be executed outside of Main thread!)

                if (request.isSuccessful) {
                   //asign items to ViewModel

                } else {
                    //asign error to ViewModel
                }
            }

        } catch (e: Exception) {
            withContext(Dispatchers.Main) {
                //asign error to ViewModel
            }
        }
    }
}

2 个答案:

答案 0 :(得分:1)

您可以将递延的工作变成变量,然后在您的主调度员上等待它,如下所示:

try {
        //Rather than await here, you take your Job as Deffered
        val request: Deferred? = RestClient.instance.getItems()

        withContext(Dispatchers.Main) {
            //Yes, you can await here because it's non-blocking call and can be safely obtained from here once completed
            val result = request?.await()
            if (request.isSuccessful) {
               //asign items to ViewModel

            } else {
                //asign error to ViewModel
            }
        }

    } catch (e: Exception) {
        withContext(Dispatchers.Main) {
            //asign error to ViewModel
        }
    }
  

关于await()的官方文档是什么:

等待该值完成而不会阻塞线程,并在延迟计算完成后恢复,返回结果值引发相应的异常如果延期取消了。

该暂停功能是可以取消的。如果在等待此挂起功能的过程中当前协程的作业被取消或完成,则此功能将立即以CancellationException继续运行。

此函数可用于通过onAwait子句进行选择调用。使用isCompleted无需等待即可检查此递延值的完成。

答案 1 :(得分:1)

由于协程正在挂起而不是阻塞,因此无需管理它们所运行的线程。在您的情况下,改良会为您处理此问题。此外,Deferred类型实际上是热数据源。这意味着Call会在您甚至调用await之前执行。 await只是等待数据在那里。

因此,您可以直接在Main调度程序上启动。因此,您只有一个地方可以从这里致电await()

viewModelScope.launch(Dispatchers.Main) {
    try {
        val request = RestClient.instance.getItems().await()

        if (request.isSuccessful) {
            //asign items to ViewModel
        } else {
            //asign error to ViewModel
        }
    } catch (e: Exception) {
        //asign error to ViewModel
    }
}