等待结束协程

问题描述 投票:0回答:1

我需要发出请求并等待它完成才能将其值分配给变量。 像这样

nameClient = Repository(applicationContext).getClient().toString()

  1. 在此变体中,立即返回“未知”,并且只有在执行协程之后才执行。
  fun getClient(): String? {
     var nameClient: String? = "unknown"
     callsApiScope.launch {

        val deferred = async(Dispatchers.IO) {
            callsApi.getClient(GetClientDTO(phonenumberString))
        }

        val response = deferred.await()
        val body = response.body()
        ...
        nameClient = nameObject.getString("fullName")
    }


     return nameClient?: nameClient
    }
  1. 在此变体中,流程到达 job.join(),一切都结束了
fun getClient(): String? {
         var nameClient: String? = null
         runBlocking {
             val job = callsApiScope.launch {

             val deferred = async(Dispatchers.Main) {
                 callsApi.getClient(GetClientDTO("+phonenumberString"))
             }

             val response = deferred.await()
             val body = response.body()
             ...
             nameClient = nameObject.getString("fullName")

             }
         }
             job.join()
         }
     return nameClient
    }

如何等待并返回值?

android kotlin coroutine
1个回答
0
投票

您首先需要决定函数何时返回。您通常有两种选择:

  1. 异步调用结果准备好后返回。 (例如,如果 api 需要 10 秒才能返回响应,则该函数将在 10 秒后返回。调用者将被阻塞 10 秒。)

  2. 在结果准备好之前返回。 (例如,即使 api 需要 10 秒才能返回响应,也要立即返回。)

选项 1 可以返回我们需要的响应,因为我们给了它足够的时间。然而,在选项2中,它不可能返回我们需要的响应,因为我们没有给它足够的时间。因此,对于选项 2,该函数必须返回某种“未来”结果。目前还没有,但将来会的。

如果你熟悉JS中

Promise
java.util.concurrent.Future
的概念,那就是选项2应该返回的内容。

您在这里感到困惑的是,在第一个变体中,您希望代码立即返回,但您想要确切的结果。这是不可能的。你要么必须给它足够的时间,要么得到“未来”的结果。 (在协程中,“未来”结果称为

Deferred
。)

在第一个变体中,您启动了一项作业并返回了值,但没有给出作业结束的时间。因此总是返回默认值。

在第二个变体中,您确实通过在

runBlocking
块中调用挂起函数,为函数提供了足够的时间。这是一个正确的方法,通常应该有效,但我想这里的错误如下:(我可能是错的)

val deferred = async(Dispatchers.Main) {
    callsApi.getClient(GetClientDTO("+phonenumberString"))
}

如果您从

Dispatchers.Main
调用此代码,则此作业将在当前代码运行后安排。但是,由于您已经处于
Dispatchers.Main
状态,并且您正在使用
deferred.await()
阻塞线程,因此实际上造成了死锁。

您的

callsApi
可以在
deferred.await
完成后运行,但
deferred.await
只有在
callsApi
运行时才会完成。陷入了僵局。

相反,由于您已经致电

runBlocking
,因此只需拨打
val response = callsApi.getClient(GetClientDTO("+phonenumberString"))
即可。这将阻塞当前协程范围,直到获取结果为止,并将提供您需要的结果,而不会造成死锁。

但请记住,正如我在本答案开头所写的那样,您确实有两个选择。 1. 阻塞,2. 非阻塞。一般来说,如果您从主线程或不可阻塞线程调用,则应避免阻塞调用。

我通常做的是,从主线程在后台启动一个长时间运行的作业,当作业完成时,我切换回主线程。 (如果不需要,我不会切换回主线程。)

以下是事情的一般进展情况。 (对于活动,请使用

lifecycleScope
代替
viewLifecycleOwner.lifecycleScope

viewLifecycleOwner.lifecycleScope.launch {
    val result = withContext(Dispatchers.IO) {
        longRunningJob()
        // do non-UI job with the result
    }

    withContext(Dispatchers.Main) {
        // do UI job with the result
    }
}

(请注意,一般建议注入调度程序

您可能应该阅读更多有关协程如何工作以及在 Android 中使用协程的最佳实践的文章。从以下资源开始:

  1. 学习基础知识。 https://kotlinlang.org/docs/coroutines-basics.html
  2. 进行实践练习。 https://developer.android.com/codelabs/kotlin-coroutines#0
  3. 随着你熟悉协程,深入学习协程知识。 https://kotlinlang.org/docs/coroutines-guide.html
  4. 当您熟悉 Android 中的协程时,请学习最佳实践。 https://developer.android.com/kotlin/coroutines/coroutines-best-practices

如果您遇到困难,也可以联系官方的人员。 https://slack-chats.kotlinlang.org/ 对于其他 Kotlin 社区,请参阅 https://kotlinlang.org/community/

愿您与协程和 Android 的旅程愉快、愉快、压力较小。

© www.soinside.com 2019 - 2024. All rights reserved.