如何用Kotlin协程将Flow<List<User>>转换为Flow<List<UserWithNumber>>
实现方案
场景1:getNumberForUser仅返回一次数值(最常用场景)
这种场景推荐用列表并行转换的写法,效率更高,且每次上游getUsers发射新的用户列表时,都会对应输出一个转换后的List<UserWithNumber>,符合绝大多数业务需求:
import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map fun getUsersWithNumber(): Flow<List<UserWithNumber>> { return getUsers() .map { userList -> coroutineScope { // 并行请求所有用户对应的数字,减少整体耗时 userList.map { user -> async { val number = getNumberForUser(user.id).first() UserWithNumber(user, number) } }.awaitAll() } } // 按需添加,把请求逻辑切到IO线程 // .flowOn(Dispatchers.IO) }
场景2:完全对齐原RxJava写法的逻辑
如果你的getNumberForUser会持续发射数值更新,或者你需要和RxJava版本完全一致的流式展开再合并逻辑,对应实现如下:
注意:该写法如果上游
getUsers发射多组用户列表,会把所有列表的用户合并到同一个最终结果列表中,和你给出的RxJava示例行为完全一致。
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flatMapConcat import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.toList fun getUsersWithNumber(): Flow<List<UserWithNumber>> { return flow { val result = getUsers() // 等价于RxJava的flatMapIterable,把列表拆分为单个用户逐个发射 .flatMapConcat { it.asFlow() } // 等价于RxJava的flatMap,对每个用户获取数字后转换为目标类型 .flatMapConcat { user -> getNumberForUser(user.id).map { number -> UserWithNumber(user, number) } } // 等价于RxJava的toList,把所有元素收集为列表 .toList() emit(result) } }
补充说明
- 如果需要处理
getNumberForUser的数值更新,希望数字变化时自动刷新输出列表,可以把flatMapConcat替换为flatMapLatest,或者使用combine操作符组合所有用户的数值流。 - 不需要并行请求的话,把
async/awaitAll的逻辑替换为普通的遍历map即可。
内容的提问来源于stack exchange,提问作者rost
相关产品推荐
相关产品推荐

