如何等待协程结束
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
协同程序中移动 println
和 button.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
}
我在下面有一些代码。 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
协同程序中移动 println
和 button.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
}