Какой оператор Flow мне нужен?

Предположим, у меня есть 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() выполняет вызовы

Вы говорите, что вам нужен список табелей успеваемости, но хотите ли вы, чтобы новый список создавался каждый раз, когда приходит оценка какого-либо человека? Или вы хотите создать только два списка: один без оценок, а затем с оценками после того, как все они будут получены?

Tenfour04 01.05.2024 14:55

Мне нужен список табелей успеваемости, не дожидаясь оценки от getStudent. Поэтому я не хочу выдавать список каждый раз, когда приходит оценка человека, потому что это подразумевает ожидание сетевого запроса от getStudent. что-то вроде поведения объединения потоков, но один запрос зависит от другого (студент зависит от человека).

HukeLau_DABA 01.05.2024 15:01

Я понимаю, что мы хотим изначально без ожидания выдать список без оценок. Я спрашиваю: будет много запросов getStudent, поэтому вы хотите дождаться, пока все они завершатся, прежде чем отправлять второй список, или вы хотите создать много списков, отправляя их каждый раз, когда вы получаете оценку за каждый студент?

Tenfour04 01.05.2024 17:26

ОК, я добавил больше контекста в свой вопрос и пример, иллюстрирующий ваши мысли. Могу ли я попросить вас показать пример того и другого, было бы полезно сравнить их и поучиться на них.

HukeLau_DABA 01.05.2024 18:06
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
69
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я не думаю, что для этого можно использовать простой набор операторов потока, поэтому нам нужно что-то создать с помощью компоновщика 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). Я обновляю табель успеваемости, соответствующий учащемуся, с оценкой и публикую свой новый список.

HukeLau_DABA 02.05.2024 17:35

Другие вопросы по теме