如何在SharedFlow不存在指定元素时抛出NoSuchElementException
问题分析
你的代码中使用firstOrNull()会导致:当SharedFlow中没有匹配目标元素,且流未进入完成状态时,会一直挂起等待新元素。而默认的MutableSharedFlow在未调用close()前永远不会完成,所以等待不存在的元素时会无限挂起。
解决方案
要实现"元素不存在时抛出NoSuchElementException"的需求,需要做两处关键修改:
1. 让SharedFlow在发射完所有状态后进入完成状态
在填充状态的emitStates()函数末尾调用close(),标记流已结束,不再发射新元素:
private suspend fun emitStates() { _sharedFlow.emit("START") delay(100) _sharedFlow.emit("FIRST") delay(100) _sharedFlow.emit("SECOND") delay(100) _sharedFlow.emit("ENDING") delay(100) _sharedFlow.emit("IDLE") // 标记流完成,不再发射新元素 _sharedFlow.close() }
2. 修改awaitState函数,使用first()替代firstOrNull()
first()操作符会在流完成且未找到匹配元素时,自动抛出NoSuchElementException,正好符合你的需求:
private suspend fun awaitState(state: String) { _sharedFlow.filter { incomingState -> incomingState == state } .onCompletion { Log.d(TAG, "onCompletion()") } .onEmpty { Log.d(TAG, "onEmpty()") } .first() // 替换firstOrNull(),未找到元素时抛出异常 }
验证效果
- 成功场景:等待"SECOND"时,流会找到匹配元素并返回,后续代码正常执行。
- 失败场景:等待"Some"时,流完成后未找到匹配元素,
first()会抛出NoSuchElementException,被catch块捕获后执行后续逻辑。
补充说明
如果你不想关闭SharedFlow(比如流需要持续发射新元素),可以结合超时机制实现:在指定时间内未找到元素则抛出异常,示例如下:
private suspend fun awaitState(state: String, timeoutMs: Long = 5000) { try { withTimeout(timeoutMs) { _sharedFlow.filter { incomingState -> incomingState == state } .first() } } catch (e: TimeoutCancellationException) { throw NoSuchElementException("State $state not found within timeout") } }
这种方式适用于需要限制等待时长的场景,超时后会将超时异常转换为需求的NoSuchElementException。
内容的提问来源于stack exchange,提问作者Jack
相关产品推荐
相关产品推荐

