调用 flow.single() 时何时抛出 NoSuchElementException

Tho*_*ook 5 kotlin kotlin-coroutines kotlin-flow

假设我有一个像这样的 API:

interface Foo {
   val barFlow: Flow<Bar>
}
Run Code Online (Sandbox Code Playgroud)

我像这样消费它:

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

根据文档,如果流为空,则可以抛出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
}
Run Code Online (Sandbox Code Playgroud)

Gle*_*val 4

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

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

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

launch {
    val data = emptyListFlow.single()
}
Run Code Online (Sandbox Code Playgroud)