如何等待协程结束

Shk*_*kum 10 android coroutine kotlin kotlin-coroutines

我在下面有一些代码。延迟(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
}
Run Code Online (Sandbox Code Playgroud)

dan*_*ela 13

这里要理解的主要事情是协程中的代码默认是按顺序执行的。 即协程相对于“兄弟”代码异步执行,但默认情况下协程内的代码同步执行。

例如:

fun DoSometing () { 

coroutineA {
doSomethingA1()
doSomethingA2()
}

some additional code 

}
Run Code Online (Sandbox Code Playgroud)

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

这意味着,在协程中,下一段代码将在前一段代码完成后执行。因此,无论您想在协程完成后执行什么,您只需将其放在协程的末尾并声明要执行它的上下文(withContext)。

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

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

GlobalScope.launch (Dispatchers.IO) {

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

  • 我编辑了我的答案,以说明如何从协程中做到这一点。您收到错误是因为您尝试从后台线程更新 UI。 (2认同)

Tus*_*aha 7

你可以尝试这样的事情:

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}]")
} 
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)