如何等待协程结束

时间:2019-12-26 17:46:26

标签: android kotlin coroutine 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
}

5 个答案:

答案 0 :(得分:2)

为什么不在println协程中移动button.isEnabledGlobalScope.launch

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 
}

现在printlnbutton.isEnabledmain线程上运行,而test()的乐趣在Default上运行,它实际上是一个工作线程。

答案 1 :(得分:1)

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

例如:

fun DoSometing () { 

coroutineA {
doSomethingA1()
doSomethingA2()
}

some additional code 

}

例程A将相对于一些其他代码执行异步操作,但doSometingA2将在doSomethingA1完成后执行。

这意味着,在一个协程中,下一个代码将在上一个代码完成后执行。 因此,完成协程后要执行的任何操作,都只需放在协程的末尾并声明要在其中执行的上下文( withContext )。

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

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

GlobalScope.launch (Dispatchers.IO) {

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

答案 2 :(得分:1)

您可以尝试以下操作:

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
}

答案 3 :(得分:0)

在继续当前线程之前,使用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)

答案 4 :(得分:0)

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

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

注意:避免使用GlobalScope,而应提供自己的CoroutineScope