我对协同例程相对较新,我正在尝试获得Launch协同例程将实现的行为:
launch(UI) {
val v1 = someDeferredType
val v2 = v1.await()
val v3 = v2.text
}
在上面的例子中,v3将等待v2执行,然后在不阻塞主线程的情况下运行。虽然这很好,但在我的调用活动/片段中引入了延迟类型和协同例程逻辑。
我想让我的活动/片段没有具体的实现细节,比如:
fun getResponseString() : String {
launch(UI) {
val v1 = someDeferredType
val v2 = v1.await()
val v3 = v2.text
}
return v3 //This is the actual String which I need to be returned
}
这样我就可以像调用活动中的常规函数一样调用getResponseString((。
到目前为止,我遇到的唯一选项是使用runBlocking联合例程,但这与启动完全不同,它会阻塞主线程。
也许我错过了什么,或者在Kotlin中使用Co例程不可能做这样的事情吗?
不能从像getResponseString
这样的常规函数返回异步操作的结果。常规函数没有这种在不阻塞调用线程的情况下暂停执行的能力。这就是为什么Kotlin必须引入"暂停函数"的概念,所以你可以写:
suspend fun getResponseString(): String {
val v1 = someDeferredType
val v2 = v1.await()
val v3 = v2.text
return v3
}
将suspend
修饰符添加到所有异步函数(必须等待但不应阻塞UI线程的函数(中,然后仅在需要启动一些自包含异步操作的最高层使用launch(UI) { ... }
。
附言:coroutines也被拼写为"协同出游"。这是一个单词,没有破折号。例如,请参见维基百科。
正则函数和挂起函数之间的区别不仅仅是实现细节:它改变了程序的语义。使用同步代码,您知道它所做的所有操作都是在调用任何其他UI事件处理程序之前执行的。您失去了异步代码的原子性,并进入了"异步地狱"的世界,在这个世界中,您的事件处理程序相互并发运行。
Kotlin明确了这一事实,这很好:只要你的代码路径没有进入协程生成器,你就知道你有原子性的保证。你必须始终明智地选择把它丢在哪里,因为一旦你这样做了,你的程序的其他部分的复杂性就会增加。
当你写
override fun onSomething() {
val v0 = getV0()
launch(UI) {
val v1 = getV1Async()
val v2 = v1.await()
useV2ToUpdateTheGUI(v2)
}
val v4 = getV4()
}
这将是处理程序代码的执行顺序:
v0 = getV0()
v4 = getV4()
onSomething
处理程序返回- 运行其他一些处理程序
v1 = getV1Async()
- 运行其他一些处理程序
v2 = v1.await()
useV2ToUpdateTheGUI(v2)
在onSomething
处理程序返回3之后,将运行大量未知代码。在上面最臭名昭著的是,您自己的处理程序会运行,并且不允许任何人假设在onSomething
中启动的所有操作都已完成。每当您想要使用v2
的值时,您必须添加代码来决定如果它还没有准备好该怎么做。
您可以将launch
调用隐藏在onSomething
调用的fun
后面,但您必须在注释/文档中仔细解释,此函数只是启动一个并发任务。当然,您将无法在处理程序的主体中使用该任务的结果。
我的经验是,您应该在处理程序中显式地使用launch(UI)
,或者将方法命名为launchFooBar()
。