合并保留订单的 Kotlin 流程

Merge Kotlin flows preserving the orders

我有一个与 Kotlin 流程相关的问题。

假设有两个网络 APIs:

我要什么

  1. 使用第一个 API 获取 ID
  2. 使用第二个API获取所有用户
  3. Return要UI显示的用户列表

对我来说,这看起来像是 Kotlin 流的完美用例。我可能会这样做:

// NetworkApi.kt

interface NetworkApi {

  @GET("ids")
  suspend fun getIds(): List<Int>

  @GET("users/{id}")
  suspend fun getUser(id: Int): User
}

在我的 ViewModel 中:

class MyViewModel(private val networkApi: NetworkApi): ViewModel() {
  
  val usersLiveData = flow {
    emit(networkApi.getIds())
  }.flatMapConcat { // it: List<Int>
    val flowList = mutableListOf<Flow<User>>()
    val userList = mutableListOf<User>()
    
    for (id in it) {
      flowList.add(flow{ emit(networkApi.getUser(id)) })
    }
    flowList.merge().toList(userList)
    userList
  }.asLiveData()
}

请注意,我使用 merge 并行发送用户请求(而不是一个接一个),以缩短延迟。

但是,根据mergedocumentation,它不会是“保留元素的顺序”。

我想要享受并行化的好处,以及保留元素的顺序。请问有办法吗?

我看不出这与 Flows 有何完美契合。你请求一次,你得到一次结果。它不是不断变化的数据流。 (如果每次网络列表更改时您都自动检索新列表,那么 Flows 就有意义了。)

如果你从这些方面考虑,代码非常简单,你只需要公开一个挂起函数。

class MyViewModel(private val networkApi: NetworkApi): ViewModel() {
  
    suspend fun getUsers(): List<User> = networkApi.getIds()
        .map { async { networkApi.getUser(it) } }
        .awaitAll()

}

如果您真的想将其公开为 LiveData(只会发布一个值):

val usersLiveData: LiveData<List<User>> = MutableLiveData<List<User>>().apply {
    viewModelScope.launch {
        value = getUsers()
    }
}

或者将其公开为只有一个发布值的 SharedFlow:

val users: SharedFlow<List<User>> = MutableSharedFlow<List<User>>(replay = 1).apply {
    viewModelScope.launch { 
        emit(getUsers())
    }
}