Объединение: Как отменить издателя flatMap - PullRequest
0 голосов
/ 24 апреля 2020

Новое в объединении и реактивном программировании, поэтому помощь очень ценится.

У меня есть следующий сценарий: я хотел бы создать пользовательский интерфейс, где пользователь может фильтровать контент с помощью различных «фильтров» кнопки на странице. Когда пользователь нажимает одну из кнопок, мне нужно отбросить запрос API для получения данных.

Теперь у меня есть издатель, который предоставляет мне «состояние» этих выборов, и я мой код структурирован так:

        state
            .publisher /* sends whenever 'state' updates behind the scenes */
            .debounce(for: 1.0, scheduler: DispatchQueue.main)
            .map { /*  create some URL request */ }
            .flatMap {
                URLSession.shared.dataTaskPublisher(for: someRequest)
                    .map { $0.data }
                    .decode(type: MyResponseType.self, decoder: JSONDecoder())
        }.sink(receiveCompletion: { (completion) in
            /// cancelled
        }) { (output) in
             /// go show my results
             /// Ideally, this is only called when the most recent API call finishes!
        }.store(in: &cancellables)

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

Предпочтительно, мы каким-то образом отменяем внутренний конвейер, поэтому выполняем обработчик завершения только с самым последним событием.

Как Могу ли я «отменить» этот внутренний конвейер (запущенный dataTaskPublisher), когда новые события проходят по конвейеру без разрушения внешнего конвейера?

1 Ответ

1 голос
/ 24 апреля 2020

Вы не хотите flatMap. Вы хотите switchToLatest. Измените flatMap на обычный map, затем добавьте .switchToLatest() после него. Поскольку switchToLatest требует соответствия типов ошибок, вам также может понадобиться использовать mapError. Оператор decode выдает тип ошибки Error, поэтому вы можете mapError до Error.

Пример:

state
    .publisher /* sends whenever 'state' updates behind the scenes */
    .debounce(for: 1.0, scheduler: DispatchQueue.main)
    .map { makeURLRequest(from: $0) }
    .map({ someRequest in
        URLSession.shared.dataTaskPublisher(for: someRequest)
            .map { $0.data }
            .decode(type: MyResponseType.self, decoder: JSONDecoder())
    })
    .mapError { $0 as Error }
    .switchToLatest()
    .sink(
        receiveCompletion: ({ (completion) in
            print(completion)
            /// cancelled
        }),
        receiveValue: ({ (output) in
            print(output)
            /// go show my results
            /// Ideally, this is only called when the most recent API call finishes!
        }))
    .store(in: &cancellables)
...