协程 - 避免使用挂起函数

问题描述

我是 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")
        }
    }

}

如果我让 #1 挂起函数 #2 需要一个不必要的回调。例子

// 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 所见),因此无需使用任何其他线程。