【发布时间】:2020-04-13 09:39:48
【问题描述】:
我有一个List<Flow<T>>,并想生成一个Flow<List<T>>。这几乎是 combine 所做的——除了 combine 等待每个 Flow 发出一个初始值,这不是我想要的。以这段代码为例:
val a = flow {
repeat(3) {
emit("a$it")
delay(100)
}
}
val b = flow {
repeat(3) {
delay(150)
emit("b$it")
}
}
val c = flow {
delay(400)
emit("c")
}
val flows = listOf(a, b, c)
runBlocking {
combine(flows) {
it.toList()
}.collect { println(it) }
}
使用combine(因此按原样),这是输出:
[a2, b1, c]
[a2, b2, c]
而我也对所有中间步骤感兴趣。这就是我想要从这三个流程中得到的:
[]
[a0]
[a1]
[a1, b0]
[a2, b0]
[a2, b1]
[a2, b1, c]
[a2, b2, c]
现在我有两个变通办法,但都不是很好...第一个很丑陋,不适用于可为空的类型:
val flows = listOf(a, b, c).map {
flow {
emit(null)
it.collect { emit(it) }
}
}
runBlocking {
combine(flows) {
it.filterNotNull()
}.collect { println(it) }
}
通过强制所有流发出第一个不相关的值,combine 转换器确实被调用,并让我删除我知道不是实际值的空值。对此进行迭代,更具可读性但更重:
sealed class FlowValueHolder {
object None : FlowValueHolder()
data class Some<T>(val value: T) : FlowValueHolder()
}
val flows = listOf(a, b, c).map {
flow {
emit(FlowValueHolder.None)
it.collect { emit(FlowValueHolder.Some(it)) }
}
}
runBlocking {
combine(flows) {
it.filterIsInstance(FlowValueHolder.Some::class.java)
.map { it.value }
}.collect { println(it) }
}
现在这个工作得很好,但我还是觉得我做得过火了。协程库中是否缺少我的方法?
【问题讨论】:
-
您可以将
FlowValueHolder替换为class Holder<T>(val value: T),您的流程将为Flow<Holder<T>?>。这会将您的第一个更简单的示例升级为可为空的T。 -
哦,是的,这是一个好点!我仍然希望看到更好的东西,但这是一个很好的改进,谢谢!
-
@MarcPlano-Lesay 我认为你不能比第一种方法做得更好,尽管我不会考虑
[]和中间步骤,因为实际上没有发出任何内容。 -
@WilliMentzel 关于
[],这是一个公平的观点。在我的具体情况下,我并不真正关心发出一个空列表,但实际上没有任何东西会更有意义。我目前正在使用稍微改进的版本,我想我会在今天晚些时候将其作为答案发布:-) -
@MarcPlano-Lesay 我也发布了一个答案:)。请看一下
标签: kotlin kotlin-coroutines kotlin-flow