如何消费返回不同Result类型的两个Kotlin协程的执行结果?
方案解答
场景适配性
这个场景完全适合使用Kotlin Flow实现,尤其是如果你后续需要监听本地存储变化、自动更新菜单状态的话,Flow的响应式特性能很好地匹配UI层的状态消费需求。如果只是单次拉取菜单数据,用原生协程async也能实现,但Flow可以更方便地和现有UI层的响应式架构对齐。
现有实现的问题
你当前的写法有两处核心逻辑漏洞:
flowOf(coroutine1.await(), coroutine2.await())会在Flow构造阶段就同步执行两次await,两个协程是串行执行的,不仅浪费时间,也失去了Flow冷流懒加载的特性- 你依赖自定义
Result.Fail标识错误而非抛出异常,现有逻辑里没有把Fail状态转换成对应的UiState,也不会触发Flow的终止逻辑,错误无法传递到下游
修正后的实现方案
核心思路
- 并行启动两个协程访问本地存储,减少总耗时
- 统一封装Result的错误判断逻辑,任意任务返回Fail直接终止流程,返回对应错误UiState
- 两个任务都成功后,分别调用各自的映射规则转换成MenuItem,合并后返回成功状态
代码示例
首先你可以先自定义一个简单的存储异常类,用来统一识别业务错误:
class StorageException : RuntimeException()
实现Flow逻辑:
sealed class UiState { data class MenuRetrieved(val menuItems: List<MenuItem>) : UiState object StorageError : UiState object UnknownError : UiState } // 扩展函数统一处理Result错误判断,返回成功结果或者抛出对应异常 fun <T> Result<T>.unwrap(): T = when (this) { is Result.Fail -> throw StorageException() is Result.Success -> this.data } val uiStateFlow = flow { // 并行启动两个IO协程,不会提前阻塞 val task1 = async(Dispatchers.IO) { coroutine1() } val task2 = async(Dispatchers.IO) { coroutine2() } // 等待两个结果,统一校验错误 val resultA = task1.await().unwrap() val resultB = task2.await().unwrap() // 分别用不同规则映射成MenuItem val menuItems = resultA.mapToMenuItems() + resultB.mapToMenuItems() emit(UiState.MenuRetrieved(menuItems)) }.catch { e -> // 捕获异常转换成对应UiState emit(when(e) { is StorageException -> UiState.StorageError else -> UiState.UnknownError }) }.flowOn(Dispatchers.IO) // UI层直接收集即可 uiStateFlow.collect { uiState -> // 处理UI状态更新 }
如果你不想用抛出异常的方式处理自定义Fail,也可以直接判断结果:
val uiStateFlow = flow { val task1 = async(Dispatchers.IO) { coroutine1() } val task2 = async(Dispatchers.IO) { coroutine2() } val resultA = task1.await() if (resultA is Result.Fail) { emit(UiState.StorageError) return@flow } val resultB = task2.await() if (resultB is Result.Fail) { emit(UiState.StorageError) return@flow } val menuItems = (resultA as Result.Success<A>).data.mapToMenuItems() + (resultB as Result.Success<B>).data.mapToMenuItems() emit(UiState.MenuRetrieved(menuItems)) }.catch { emit(UiState.UnknownError) } .flowOn(Dispatchers.IO)
额外说明
因为两个任务都是访问本地存储,属于IO密集型操作,并行执行不会有额外性能损耗,反而能大幅降低总等待时间。如果后续需要监听本地存储的变更自动刷新菜单,只需要把两个协程替换成对应的Flow,用combine算子合并两个流即可,整体逻辑不需要做太大改动。
内容的提问来源于stack exchange,提问作者xavierdominguez
相关产品推荐
相关产品推荐

