假设我有 getPeople():
List<Person>
和 getStudent(ssn: String): Student
data class Person(val name: String, val ssn: String)
data class Student(val ssn: String, val grade: Char)
data class ReportCard(val name: String, val grade: Char)
我想要一张
List<ReportCard>
,但我不想等待 getStudent 形成成绩单。例如,getPeople() 返回 [("Joy", "111"), ("Tom", "123"), ("Jerry", "567")]。假设获得的成绩是:
乔伊:C 杰瑞:F 汤姆:A
我将通过 ssn 加入 Person with a Student 以在可滚动列表中形成 ReportCard,因此我希望异步填写 ReportCard 中的字段。如果发生错误,我会忽略它并继续。
每当返回成绩时发出:
[(“乔伊”, ''), (“汤姆”, ''), (“杰瑞”, '')] <--getPeople() done
[(“乔伊”, 'A'), (“汤姆”, ''), (“杰瑞”, '')] <--getStudent(Joy) done
[(“乔伊”, 'A'), (“汤姆”, ''), (“杰瑞”, 'F')] <--getStudent(Jerry) done
[(“乔伊”, 'A'), (“汤姆”, ''), (“杰瑞”, 'F')] <--getStudent(Tom) error
成绩完成后会发出另一种解决方案:
[(“乔伊”, ''), (“汤姆”, ''), (“杰瑞”, '')] <--getPeople() done
[(“乔伊”, 'A'), (“汤姆”, ''), (“杰瑞”, 'F')] <--getStudent() calls done
我认为没有一组简单的流运算符可以用于此目的,因此我们需要使用
flow
构建器构建一些东西来获得该功能。
此外,不存在像示例解决方案中那样的空 Char 之类的东西,因此我将使用可为空的
Char
和 null
来表示没有可用的等级。
假设这些是您可用的输入:
val people: Flow<List<Person>> = //...
suspend fun getGradeForSsn(ssn: String): Char? { //...
首先,为了简化流程,创建此函数的错误捕获版本:
suspend fun getGradeForSsnOrNull(ssn: String): Char? =
try {
getGradeForSsn(ssn)
} catch (e: CancellationException) {
throw(e)
} catch (e: Exception) {
null
}
我还没有测试过这些解决方案,但也许它们可以成为您构建可行的东西的开始。
然后,对于第二种情况,您只为每个输入人员列表发出两个成绩单列表,您可以这样做:
val reportCards = people.flatMapLatest { peopleList ->
flow {
emit(peopleList.map { ReportCard(it.name, null) })
val finalList = coroutineContext {
peopleList.map {
async { ReportCard(it.name, getGradeForSsnOrNull(it.ssn)) }
}.awaitAll()
}
emit(finalList)
}
}
第一种情况比较复杂。也许是这样的:
val reportCards = people.flatMapLatest { peopleList ->
flow {
val reportCardsBySsn = peopleList.associate {
it.ssn to ReportCard(it.name, null)
}
emit(reportCardsBySsn.values.toList())
coroutineContext {
peopleList.forEach {
val grade = getGradeForSsnOrNull(it.ssn)
if (grade != null) {
reportCardsBySsn[ssn] = reportCardsBySsn[ssn].copy(grade = grade)
emit(reportCardsBySsn.values.toList())
}
}
}
}
}