Предположим, у меня есть 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() возвращает [("Джой", "111"), ("Том", "123"), ("Джерри", "567")]. Предположим, что полученные оценки составили:
Радость: С Джерри: Ф Том: А
Я собираюсь присоединиться к человеку со студентом по ssn, чтобы сформировать ReportCard в прокручиваемом списке, поэтому я хочу, чтобы поля в ReportCard заполнялись асинхронно. Если возникает ошибка, я игнорирую ее и возобновляю работу.
Испускание всякий раз, когда возвращается оценка:
[("Джой", ''), ("Том", ''), ("Джерри", '')] <--getPeople() выполнено
[("Джой", 'A'), ("Том", ''), ("Джерри", '')] <--getStudent(Joy) сделано
[("Джой", 'A'), ("Том", ''), ("Джерри", 'F')] <--getStudent(Jerry) Done
[("Джой", 'A'), ("Том", ''), ("Джерри", 'F')] <--getStudent(Tom) ошибка
Другое решение — выдача после завершения оценок:
[("Джой", ''), ("Том", ''), ("Джерри", '')] <--getPeople() выполнено
[("Joy", 'A'), ("Tom", ''), ("Jerry", 'F')] <--getStudent() выполняет вызовы
Мне нужен список табелей успеваемости, не дожидаясь оценки от getStudent. Поэтому я не хочу выдавать список каждый раз, когда приходит оценка человека, потому что это подразумевает ожидание сетевого запроса от getStudent. что-то вроде поведения объединения потоков, но один запрос зависит от другого (студент зависит от человека).
Я понимаю, что мы хотим изначально без ожидания выдать список без оценок. Я спрашиваю: будет много запросов getStudent, поэтому вы хотите дождаться, пока все они завершатся, прежде чем отправлять второй список, или вы хотите создать много списков, отправляя их каждый раз, когда вы получаете оценку за каждый студент?
ОК, я добавил больше контекста в свой вопрос и пример, иллюстрирующий ваши мысли. Могу ли я попросить вас показать пример того и другого, было бы полезно сравнить их и поучиться на них.
Я не думаю, что для этого можно использовать простой набор операторов потока, поэтому нам нужно что-то создать с помощью компоновщика 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())
}
}
}
}
}
ок, я ценю это. Я сделал нечто подобное, создав собственный поток. Я собрал людей и выдал табель успеваемости Placeholder с именем. Для каждого человека я вызываю getStudent(ssn). Я обновляю табель успеваемости, соответствующий учащемуся, с оценкой и публикую свой новый список.
Вы говорите, что вам нужен список табелей успеваемости, но хотите ли вы, чтобы новый список создавался каждый раз, когда приходит оценка какого-либо человека? Или вы хотите создать только два списка: один без оценок, а затем с оценками после того, как все они будут получены?