[英]How to wait for end of a coroutine
I have some code below.我在下面有一些代码。 Delay (3000) is just replacement for a long loop (or cycle).
延迟(3000)只是长循环(或循环)的替代。 I'm expecting that after completion of loop
println(res)
will print “Some String” and then enable button
.我期待循环完成后
println(res)
将打印“Some String”,然后启用button
。 But in real life println(res)
prints an empty string and button
became enabled at same time when I click it.但在现实生活中,
println(res)
打印一个空字符串,并且当我单击它时button
同时启用。 My question is: how I can wait for end of a coroutine and only after completion of the coroutine run println(res)
and button.isEnabled = true
.我的问题是:如何等待协程结束,并且只有在协程完成后才运行
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
}
The main thing to understand here is that code within coroutine is by default executed sequentially.这里要理解的主要事情是协程中的代码默认是按顺序执行的。 Ie coroutine is executed asynchronously in relation to "sibling" code, but code within coroutine executes synchronously by default.
即协程相对于“兄弟”代码异步执行,但默认情况下协程内的代码同步执行。
For example:例如:
fun DoSometing () {
coroutineA {
doSomethingA1()
doSomethingA2()
}
some additional code
}
Corroutine A will execute async in relation to some additional code but doSometingA2 will be executed after doSomethingA1 is done. Corroutine A 将执行与一些附加代码相关的异步操作,但 doSometingA2 将在 doSomethingA1 完成后执行。
That means, that within a coroutine every next piece of code will be executed after the previous one is done.这意味着,在协程中,下一段代码将在前一段代码完成后执行。 So, whatever you want to execute when coroutine is done, you just put at the end of that coroutine and declare context ( withContext ) in which you want to execute it.
因此,无论您想在协程完成后执行什么,您只需将其放在该协程的末尾并声明要在其中执行它的上下文( withContext )。
The exception is of course if you start another async piece of code within coroutine (like another coroutine).当然,如果您在协程中启动另一段异步代码(如另一个协程),则例外。
EDIT: If you need to update UI from the coroutine, you should execute that on the main context, ie you'll have something like this:编辑:如果您需要从协程更新 UI,您应该在主上下文中执行它,即您将拥有如下内容:
GlobalScope.launch (Dispatchers.IO) {
//do some background work
...
withContext (Dispatchers.Main) {
//update the UI
button.isEnabled=true
...
}
}
You can try some thing like this:你可以尝试这样的事情:
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 will wait for the coroutine to finish and then run code below it 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
}
why you don't move println
and button.isEnabled
inside GlobalScope.launch
coroutine.为什么你不在
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
}
}
if you whant your code run on main
thread add Dispatchers.Main
as an argument.如果您希望您的代码在
main
线程上运行,请添加Dispatchers.Main
作为参数。
GlobalScope.launch(Dispatchers.Main) {
val res = withContext(Dispatchers.Default) {
test()
}
println(res)
button.isEnabled = true
}
now println
and button.isEnabled
run on main
thread and test()
fun runs on Default
which in real is a worker thread.现在
println
和button.isEnabled
在main
线程上运行,而test()
fun 在Default
上运行,它实际上是一个工作线程。
Use the Job.join(): Unit
method to wait for a coroutine to finish before continuing with current the thread:使用
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
is for situations where you don't care about the result outside the coroutine. launch
适用于您不关心协程之外的结果的情况。 To retrieve the result of a coroutine use async
.要检索协程的结果,请使用
async
。
val res = GlobalScope.async(Dispatchers.Default) { test() }.await()
Note: avoid using GlobalScope
, provide your own CoroutineScope
instead.注意:避免使用
GlobalScope
,而是提供您自己的CoroutineScope
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.