问题描述
我是 KMM 和协程的新手。有没有一种方法可以等待异步函数的响应而无需使依赖函数也挂起?
代码示例
// In HttpClient.kt in commonMain
class MyHttpClient {
val client = HttpClient()
suspend fun get(url: String): String {
client.get<String>(url)
}
}
// In Another class in commonMain
class Foo {
private val httpClient = MyHttpClient()
fun performAction() { <--- #1
val data = httpClient.get("server url")
// So stuff with that data after its retrieve from server.
}
}
// In iOS swift code
struct Example: View {
var body: some View {
Button {
foo.performAction() <--- #2
} label: {
Text("Click Me")
}
}
}
// In iOS swift code
struct Example: View {
var body: some View {
Button {
foo.performAction(completionHandler: handler)
} label: {
Text("Click Me")
}
}
private func handler(response: KotlinUnit?,error: Error?) {
// DO nothing
}
}
我的单元测试也失败了,因为你不能让测试挂起函数,而且 runBlocking
不在 commonMain 中。
解决方法
您需要指定一个协程,您的方法应该在该协程上运行。
class Foo {
private val httpClient = MyHttpClient()
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
fun performAction() {
scope.launch {
val data = httpClient.get("server url")
// So stuff with that data after its retrieve from server.
}
}
}
,
您不需要添加完成回调。你所要做的就是从点击处理程序启动一个协程,就像这样:
Button {
viewScope.launch {
foo.performAction()
// add GUI code here that runs when action is done
}
} label: {
Text("Click Me")
}
如果您当前没有定义协程范围,您应该像这样(大约)添加它:
struct Example: View {
private val viewScope = CoroutineScope(Dispatchers.Main)
// adapt this to the actual way you get the "view closed" event
fun onClose() {
viewScope.cancel()
}
...
}
Dispatchers.Main
使您的协程在 GUI 线程上运行。由于您使用的是可挂起的 IO(如您的 suspend fun get(url: String): String
所见),因此无需使用任何其他线程。