KotlinconsumeEach()与receive()的比较

lvmkulzt  于 2022-11-16  发布在  Kotlin
关注(0)|答案(1)|浏览(192)

我有一些代码的格式:

class MyProducerClass {
        fun CoroutineScope.myProducerFunction(): ReceiveChannel<someClass> = produce {
            // send some stuff to a channel
        }
    }

    class MyConsumerClass {
        val producerObject = MyProducerClass()

        fun CoroutineScope.myConsumerFunction(channel: ReceiveChannel<someClass>) = launch {
            // Consume stuff from the channel
        }
        
        fun functionToBeCalledByApplication() = runBlocking {
            MyProducerClass().myConsumerFunction(myProducerFunction())
        }
    }

但是看起来myProducerFunction不能从MyConsumerClass中访问。有人能解释一下为什么会这样吗?我对Kotlin很陌生,如果能有一些关于为什么协程不允许这种类型的模式的直觉会很有帮助
把所有与协程相关的函数都移到一个类中,它工作了。仍然不确定为什么跨类的协程函数调用不工作

monwx1rj

monwx1rj1#

myProducerFunction()是CoroutineScope的一个扩展函数,所以只能在CoroutineScope上调用它。
这就导致了一个尴尬的局面:当你在一个类中定义一个扩展函数时,从这个类的外部调用它会变得很困难。为了能够调用另一个函数,你必须将它的所属类作为接收者引入作用域,这可以通过run { }作用域函数来完成,如下所示:

fun functionToBeCalledByApplication(scope: CoroutineScope) = runBlocking {
    MyProducerClass().run { scope.myConsumerFunction(myProducerFunction()) }
}

上面,我把CoroutineScope变成了一个函数参数,你也可以把它变成一个扩展函数,但是这样你就把问题传递给了想要调用它的应用程序类:

fun CoroutineScope.functionToBeCalledByApplication() = runBlocking {
    MyProducerClass().run { myConsumerFunction(myProducerFunction()) }
}

由于这些原因,您通常希望避免在类中定义公共扩展函数。在这种情况下,根据这些类的使用方式,我将提供CoroutineScope作为关联类的成员属性,或者使其成为这些函数中的常规函数参数。
很快,这个问题就会得到解决。Kotlin中有一个实验性的特性,叫做“上下文接收器”,我认为它计划在Kotlin 1.8或1.9中变得稳定。你将能够为一个函数定义一个上下文接收器,这样接收器需要在作用域中调用一个函数,但是你不必在那个对象上调用这个函数。有了这个即将到来的特性,函数的定义如下:

class MyProducerClass {
    context(CoroutineScope)
    fun myProducerFunction(): ReceiveChannel<someClass> = produce {
            // send some stuff to a channel
    }
}

class MyConsumerClass {
    val producerObject = MyProducerClass()

    context(CoroutineScope)
    fun myConsumerFunction(channel: ReceiveChannel<someClass>) = launch {
        // Consume stuff from the channel
    }

    context(CoroutineScope)
    fun functionToBeCalledByApplication() = runBlocking {
        MyProducerClass().myConsumerFunction(myProducerFunction())
    }
}

现在CorouineScope只需要作为一个接收器就可以调用这些函数,而不是必须作为调用函数的对象。因此,在Application类中,如果你在一个协程函数(如launch)中调用函数,它将工作得很好,因为CoroutineScope是一个接收器。
附带说明,您几乎不会在生产代码中看到runBlocking,因为它破坏了使用协程的目的,它只用于桥接非基于协程的代码和协程,比如,当您使用一个处理自己的多线程的库,但需要能够从该库的后台线程调用suspend函数时。把最后一个函数改为suspend函数会更合适。调用它的应用程序代码无论如何都需要提供一个CoroutineScope,它最好在自己的协程中完成。

相关问题