首页 > 解决方案 > 何时为调用 flow.single() 引发 NoSuchElementException

问题描述

假设我有一个这样的 API:

interface Foo {
   val barFlow: Flow<Bar>
}

我像这样消费它:

class FooConsumer(private val foo: Foo) {
   init {
       CoroutineScope(Dispatchers.IO).launch {
           val bar = foo.barFlow.single()
           println("Collected bar: $bar)
       }
   }
}

根据文档,如果流为空,则可以抛出singlea 。NoSuchElementException但是,这让我很困惑,因为对流的终端操作将“等待”要发出的流元素。那么调用如何single知道流中没有元素呢?也许一个元素还没有发出?

我的意思是在引擎盖下,调用single是在进行检查之前收集源流。因此,在执行 null 检查之前必须至少发出 1 个项目,因此 null 检查永远不应该成功并且NoSuchElementException永远不应该抛出 a(对于流是不可为 null 类型的情况)。

那么NoSuchElementException只有可空类型的流才有可能吗?

这是源代码single

/**
 * The terminal operator, that awaits for one and only one value to be published.
 * Throws [NoSuchElementException] for empty flow and [IllegalStateException] for flow
 * that contains more than one element.
 */
public suspend fun <T> Flow<T>.single(): T {
    var result: Any? = NULL
    collect { value ->
        if (result !== NULL) error("Expected only one element")
        result = value
    }

    if (result === NULL) throw NoSuchElementException("Expected at least one element")
    @Suppress("UNCHECKED_CAST")
    return result as T
}

标签: kotlinkotlin-coroutineskotlin-flow

解决方案


NoSuchElementException当 Flow 完成其发射而不发射单个元素时抛出。我现在能想到的一种情况是当您需要将集合转换为流源时。如果该集合为空并且您调用single该 Flow,您将获得一个NoSuchElementException.

这个例子可能看起来很荒谬,但你明白了:

val emptyListFlow = emptyList<Int>().asFlow()

launch {
    val data = emptyListFlow.single()
}

推荐阅读