寻找适配异步生成ReportCard的Kotlin Flow运算符
两种发射方式对应的Flow实现方案
场景1:每次获取到成绩就发射更新后的列表
这种场景适合滚动列表即时刷新,每拿到一个学生的成绩就推送一次完整的更新后列表。
核心逻辑:
- 先发射初始空成绩列表(从
getPeople()生成,grade用空字符占位) - 对每个
Person并行发起getStudent()请求,捕获异常并忽略失败的请求 - 每次拿到有效成绩时,更新对应位置的
ReportCard并发射完整列表
实现代码(结合Flow运算符):
fun getReportCardsFlow(): Flow<List<ReportCard>> = flow { // 1. 发射初始列表 val initialCards = getPeople().map { ReportCard(it.name, ' ') } emit(initialCards) // 2. 为每个Person创建异步获取成绩的Flow val updateFlows = getPeople().map { person -> flow { runCatching { getStudent(person.ssn) } .onSuccess { student -> // 更新对应ReportCard的成绩 val updatedCards = initialCards.map { if (it.name == person.name) it.copy(grade = student.grade) else it } emit(updatedCards) } // 忽略错误,不触发更新 }.flowOn(Dispatchers.IO) } // 3. 合并所有更新Flow,有结果就发射 merge(*updateFlows.toTypedArray()).collect { emit(it) } }
关键Flow运算符:
flow:自定义Flow构建器,控制初始列表和后续更新的发射时机flowOn:指定异步请求的调度线程(IO线程)merge:合并多个更新Flow,任何一个Flow产生结果时都会触发收集
场景2:所有成绩获取完成后再发射最终列表
这种场景仅发射两次结果:初始空列表、所有请求完成后的最终列表,适合不需要实时刷新的场景。
核心逻辑:
- 先发射初始空成绩列表
- 并行发起所有
getStudent()请求,收集所有成功获取的成绩 - 批量更新列表后发射最终结果
实现代码(结合Flow运算符):
fun getFinalReportCardsFlow(): Flow<List<ReportCard>> = flow { val initialCards = getPeople().map { ReportCard(it.name, ' ') } // 1. 发射初始列表 emit(initialCards) // 2. 并行处理所有请求,收集有效成绩映射 val gradeMap = coroutineScope { getPeople().map { person -> async(Dispatchers.IO) { runCatching { getStudent(person.ssn) } .getOrNull()?.let { person.ssn to it.grade } } }.awaitAll().filterNotNull().toMap() } // 3. 生成最终列表并发射 val finalCards = initialCards.map { card -> val person = getPeople().first { it.name == card.name } card.copy(grade = gradeMap[person.ssn] ?: ' ') } emit(finalCards) }
关键Flow相关实现:
flow:自定义Flow构建器,控制两次结果的发射时机- 结合协程
async/awaitAll实现并行请求(若getStudent返回Flow,可替换为flatMapMerge批量处理)
补充提示
- 错误处理统一用
runCatching或Flow的catch运算符,确保单个请求失败不中断整体流程 - 若
getStudent本身是返回Flow的方法,可直接替换为getStudent(person.ssn).catch {}.map { ... }处理异步和错误
内容的提问来源于stack exchange,提问作者HukeLau_DABA
相关产品推荐
相关产品推荐

