如何等待协程结束

How to wait for end of a coroutine

我在下面有一些代码。 Delay (3000) 只是一个长循环(或循环)的替代品。我期望在循环完成后 println(res) 将打印“Some String”,然后启用 button。但在现实生活中 println(res) 打印一个空字符串,当我单击它时 button 同时启用。 我的问题是:如何等待协程结束并且仅在协程完成后 运行 println(res)button.isEnabled = true.

private var res: String = ""

private suspend fun test(): String {
    delay(3000) // delay - just replacement for long loop
    return "Some String" // String received after loop
}

fun onClick(view: View) {
    res = ""
    button.isEnabled = false
    GlobalScope.launch {
        res = withContext(Dispatchers.Default) {
            test()
        }
    }
    println(res) // 1. trying to get string received after loop, but not working
    button.isEnabled = true // 2. button must be enabled after loop in cycle, but it's not waiting till end of loop
}

为什么你不在 GlobalScope.launch 协同程序中移动 printlnbutton.isEnabled

fun onClick(view: View) {
    res = ""
    button.isEnabled = false
    GlobalScope.launch {
        val res = withContext(Dispatchers.Default) {
            test()
        }

        println(res)
        button.isEnabled = true 
    }
}

如果您希望 运行 在 main 线程上添加 Dispatchers.Main 作为参数。

GlobalScope.launch(Dispatchers.Main) {
   val res = withContext(Dispatchers.Default) {
            test()
        }

        println(res)
        button.isEnabled = true 
}

now println and button.isEnabled 运行 on main thread 和 test() fun 运行s on Default which in real 是工作线程。

使用 Job.join(): Unit 方法等待协程完成,然后再继续当前线程:

//launch coroutine
var result = ""
val request = launch {
    delay(500)
    result = "Hello, world!"
}

//join coroutine with current thread
request.join()

//print "Hello, world!"
println(result)

launch 适用于您不关心协程外结果的情况。要检索协程的结果,请使用 async

val res = GlobalScope.async(Dispatchers.Default) { test() }.await()

注意:避免使用 GlobalScope,请提供您自己的 CoroutineScope

这里主要要理解的是协程内的代码默认是顺序执行的 IE。协程相对于 "sibling" 代码异步执行,但协程内的代码默认同步执行。

例如:

fun DoSometing () { 

coroutineA {
doSomethingA1()
doSomethingA2()
}

some additional code 

}

Corroutine A 将执行与 一些额外代码 相关的异步,但 doSometingA2 将在 doSomethingA1 完成后执行。

这意味着,在协程中,每一段代码都将在前一段代码完成后执行。 所以,无论你想在协程完成时执行什么,你只要放在那个协程的末尾并声明你想要执行它的上下文(withContext)。

当然,如果您在协程中启动另一段异步代码(如另一个协程),则例外。

编辑:如果您需要从协程更新 UI,您应该在主上下文中执行它,即您将拥有如下内容:

GlobalScope.launch (Dispatchers.IO) {

   //do some background work
   ...
   withContext (Dispatchers.Main) { 
       //update the UI 
       button.isEnabled=true  
       ...
     }
}

你可以尝试这样的事情:

suspend fun saveInDb() {
    val value = GlobalScope.async {
       delay(1000)
       println("thread running on [${Thread.currentThread().name}]")
       10
    }
    println("value =  ${value.await()} thread running on [${Thread.currentThread().name}]")
} 

await 将等待协程完成,然后 运行 下面的代码

fun onClick(view: View) {
    res = ""
    button.isEnabled = false
    GlobalScope.launch(Dispatchers.Main){ // launches coroutine in main thread
         updateUi()
    }
}

suspend fun updateUi(){
    val value = GlobalScope.async { // creates worker thread
       res = withContext(Dispatchers.Default) {
          test()
       }
    }
    println(value.await()) //waits for workerthread to finish
    button.isEnabled = true //runs on ui thread as calling function is on Dispatchers.main
}