从协程调用的回调函数未与KotlinJS一起运行

u91tlkcl  于 2023-01-09  发布在  Kotlin
关注(0)|答案(1)|浏览(91)

我正在尝试编写一个Kotlin函数来执行HTTP请求,然后将结果返回给JavaScript。
因为使用IR编译器时,我无法使用JavaScript中的挂起函数,所以我尝试使用回调函数来代替。
然而,当从协程调用时,回调永远不会执行。
下面是我正在做的一个小例子:

private val _httpClient = HttpClient(JsClient()) {
    install(ContentNegotiation) { json() }
    defaultRequest { url(settings.baseUrl) }
}

fun requestJwtVcJsonCredential(
        request: JSJwtVcJsonVerifiableCredentialRequest,
        callback: (JSDeferredJsonCredentialResponse?, JSJwtVcJsonVerifiableCredentialResponse?, Any?) -> Unit
    ) {
    CoroutineScope(_httpClient.coroutineContext).launch {
        // call suspend function
        val response = requestCredential(convert(request))
        // this never runs, even though the coroutine does run successfully
        println("Coroutine received: $response")
        callback(response.first, response.second, response.third)
    }

}

我注意到this question在Android中也有类似的问题,但建议的修复不适用于JavaScript......具体来说,使用Channel对我的情况没有帮助,因为我没有一个协程来接收,并试图启动一个新的协程来从通道接收,然后从该协程调用回调,也不起作用(根本问题似乎是我不能从任何协程调用回调函数)。
解决这个问题的最好方法是什么?假设我需要调用的函数是一个suspend函数(HTTP客户端函数),我不能改变它,但是我可以改变它周围的一切,使它从一个非suspend函数工作(因为这是KotlinJS的一个限制)。

pbpqsu0x

pbpqsu0x1#

根本问题是suspend函数实际上失败了,但似乎没有默认的异常处理程序,因此异常没有记录在任何地方,导致函数静默失败,使回调看起来像是被调用但没有执行。
但是,我认为值得一提的是,KotlinJS支持Promise<T>,因此向JS公开suspend函数的更好方法是实际编写一个“适配器”函数,该函数返回Promise
CouroutineScope上有一个promise extension function可用于此目的。
例如,如果你有一个Kotlin函数:

suspend fun makeRequest(request: Request): Response

要在JavaScript中公开它,可以使用如下适配器函数:

@JsExport
fun makeRequestJS(request: Request): Promise<Response> {
    // KTor's HttpClient itself is a CoroutineScope
    return _httpClient.promise { makeRequest(request) }
}

这避免了引入回调函数的需要。

相关问题