简体   繁体   English

为什么我不能在 lambda 函数下调用 kotlin 挂起函数

[英]Why can't I call kotlin suspend function under lambda function

Let me start with example code snippets让我从示例代码片段开始

suspend fun executeLive(result: MutableLiveData<Person>) {

    val response = ... //suspend api request

    mediatorLiveData.removeSource(response)

    mediatorLiveData.addSource(response) {
        result.value = sortData(it) // sortData is also suspend function which sortData at Dispatcher.Default
    }

}

In this example, sortData can't call under lambda function (in this case addSource ).And also I already declare executeLive as suspend , that why suspend api request can start at first.在这个例子中, sortData不能在lambda functionlambda function (在这种情况下是addSource )。而且我已经将executeLive声明为suspend ,这就是为什么suspend api 请求可以首先启动。 But sortData function show compile time error但是sortData函数显示编译时错误

Suspend function can only be called from a coroutine body挂起函数只能从协程主体调用

So how do I change my code structure to solve this problems?那么我该如何改变我的代码结构来解决这个问题呢?

Update: Is there any article about this?更新:有没有关于这个的文章?

A lambda is generally a callback function.一个 lambda 通常是一个回调函数。 Callback functions are so called because we wrap a block of code in a function, and pass it to someone else (or some place else) to be executed.回调函数之所以这么叫,是因为我们将一段代码封装在一个函数中,并将其传递给其他人(或其他地方)来执行。 It is a basic inversion of control where the code is not for you to execute, but someone else to do it (example the framework).这是一种基本的控制反转,其中代码不是由您执行,而是由其他人来执行(例如框架)。

For example when you set a onClickListener on a button, we don't know when it will get called, we pass a lambda for the framework which takes care of the user interaction to call the specified action.例如,当您在按钮上设置onClickListener时,我们不知道它何时会被调用,我们为框架传递了一个 lambda,该框架负责调用指定操作的用户交互。

In your case similarly the suspend function is not calling the sortdata , it is passing it to the mediatorLiveData object to call it in its own context.在您的情况下,挂起函数没有调用sortdata ,而是将其传递给mediatorLiveData对象以在其自己的上下文中调用它。 It is not necessary the lambda you passed would be called from a coroutine body, as such this is not allowed.您传递的 lambda 不必从协程主体中调用,因此这是不允许的。

You can solve this by converting the mediatorLiveData.addSource call into a suspending call itself with suspendCoroutine :您可以通过使用suspendCoroutinemediatorLiveData.addSource调用转换为挂起调用本身来解决此suspendCoroutine

suspend fun executeLive(result: MutableLiveData<Person>) {

    val response = ... //suspend api request

    mediatorLiveData.removeSource(response)

    val data = suspendCoroutine<TypeOfData> { cont ->
        mediatorLiveData.addSource(response) { cont.resume(it) }
    }

    result.value = sortData(data)
}

I've used TypeOfData as a placeholder for whatever the type of data emitted by response is.无论response发出的数据类型是什么,我都使用TypeOfData作为占位符。 Note that this will only work if the you're intending for a single emission to happen, though.请注意,这仅在您打算进行单次发射时才有效。

If you need to track multiple values, you can experiment with callbackFlow :如果您需要跟踪多个值,您可以尝试使用callbackFlow

suspend fun executeLive(result: MutableLiveData<Person>) {

    val response = ... //suspend api request

    mediatorLiveData.removeSource(response)

    callbackFlow<TypeOfData> {
        mediatorLiveData.addSource(response) { offer(it) }

        awaitClose { mediatorLiveData.removeSource(response) }
    }
        .collect { result.value = sortData(it) }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM