我正在尝试为听众创建producer
。
我的代码看起来像这样
suspend fun foo() = produce{
someEvent.addListener {
this.send(it)
}
}
但我收到错误Suspension functions can be called only within coroutine
这是有道理的。我的问题是。有没有办法使用协同程序来实现这种模式?
答案 0 :(得分:3)
There are several ways to implement it, depending on what you are trying to achieve:
If you want to receive just the most recent event, then you should use a conflated channel and offer
method that aways succeeds for it:
fun foo() = produce<T>(capacity = Channel.CONFLATED) {
someEvent.addListener {
offer(it)
}
}
If it is critical to receive all events, then your choices depend on the behavior of your event producer. The key question to ponder here is what happens if your event producer starts producing lots of events "non-stop". Most "synchronous" event producers, as a rule of thumb, do not support an explicit back-pressure signal, but they still support an implicit back-pressure signal -- they will slow down if their listeners are slow or block the thread. So, usually, the following solution works perfectly for synchronous event producers:
fun foo() = produce<T>() {
someEvent.addListener {
runBlocking { send(it) }
}
}
You can also specify some positive capacity = xxx
as parameter to produce
builder as a performance optimization if you have cases when a batch of events is produced at once and you don't want to block the producer, but let the consumer handle them at its own pace.
In the rare case when your producer does not understand an implicit blocking back-pressure signal (when it as some sort of multi-threaded contraption violently producing events without internal synchronization), then you can use channel with unlimited capacity with offer
, but beware that you risk running out memory if the producer outruns the consumer:
fun foo() = produce<T>(capacity = Channel.UNLIMITED) {
someEvent.addListener {
offer(it)
}
}
If your producer supports an explicit back-pressure signal (like functional reactive streams), then you should use a special adapter to properly transfer their back-pressure signal to/from coroutines. The kotlinx.coroutines
library has a number of out-of-the-box integration modules with various reactive libraries for this purpose. See here.
Note: you should not mark your foo
function with suspend
modifier. Invocation of foo
does not suspend invoker in anyway. It just immediately (synchronously) starts a producer coroutine.
To learn more about coroutines and different kinds of channels I highly recommend to study the guide on kotlinx.coroutines.