iOS RxSwift, как предотвратить размещение последовательности (ошибка throw)? - PullRequest
0 голосов
/ 20 сентября 2018

У меня есть последовательность, состоящая из нескольких операторов.Всего существует 7 мест, где ошибки могут генерироваться во время этой последовательности обработки.Я столкнулся с проблемой, когда последовательность не работает, как я ожидал, и я ищу элегантное решение этой проблемы:

let inputRelay = PublishRelay<Int>()
let outputRelay = PublishRelay<Result<Int>>()

inputRelay
.map{ /*may throw multiple errors*/}
.flatmap{ /*may throw error*/ }
.map{}
.filter{}
.map{ _ -> Result<Int> in ...}
.catchError{}
.bind(to: outputRelay)

Я думал, что catchError просто поймает ошибку,позвольте мне преобразовать его в результат сбоя, но предотвратите освобождение последовательности.Тем не менее, я вижу, что в первый раз, когда обнаруживается ошибка, вся последовательность освобождается и больше событий не проходит.

Без этого поведения у меня останутся пугающие результаты <> повсюду, и мне придется разветвлять мою последовательность несколько раз, чтобы направить Result.failure(Error) к выходу.Есть невосстановимые ошибки, поэтому retry(n) не вариант:

let firstOp = inputRelay
.map{ /*may throw multiple errors*/}
.share()

//--Handle first error results--
firstOp
.filter{/*errorResults only*/}
.bind(to: outputRelay)

let secondOp = firstOp
.flatmap{ /*may throw error*/ }
.share()

//--Handle second error results--
secondOp
.filter{/*errorResults only*/}
.bind(to: outputRelay)

secondOp
.map{}
.filter{}
.map{ _ -> Result<Int> in ...}
.catchError{}
.bind(to: outputRelay)

^ Это очень плохо, потому что есть около 7 мест, где могут быть выброшены ошибки, и я не могу просто продолжать ветвление последовательностикаждый раз.

Как операторы RxSwift могут перехватывать все ошибки и выдавать результат ошибки в конце, но НЕ распоряжаться всей последовательностью при первой ошибке?

Ответы [ 2 ]

0 голосов
/ 23 сентября 2018

Первый трюк, который приходит на ум, - это materialize.Это будет конвертировать каждый Observable<T> в Observable<Event<T>>, поэтому ошибка будет просто .next(.error(Error)) и не приведет к завершению последовательности.

в данном конкретном случае, хотя другой трюк будетнеобходимо.Помещение всей цепочки "триггеров" в плоскую карту, а также materialize в этот конкретный фрагмент.Это необходимо, потому что материализованная последовательность все еще может завершиться, что приведет к завершению в случае обычной цепочки, но не завершит цепочку flatMapped (как выполнено == успешно выполнено внутри flatMap).

inputRelay
    .flatMapLatest { val in
        return Observable.just(val)
            .map { value -> Int in
                if value == 1 { throw SomeError.randomError }
                return value + value
            }
            .flatMap { value in
                return Observable<String>.just("hey\(value)")
            }
            .materialize()
    }
    .debug("k")
    .subscribe()

    inputRelay.accept(1)
    inputRelay.accept(2)
    inputRelay.accept(3)
    inputRelay.accept(4)

Это выведет следующее для k:

k -> subscribed
k -> Event next(error(randomError))
k -> Event next(next(hey4))
k -> Event next(completed)
k -> Event next(next(hey6))
k -> Event next(completed)
k -> Event next(next(hey8))
k -> Event next(completed)

Теперь все, что вам нужно сделать, это отфильтровать только «следующие» события из материализованной последовательности.

Если у вас есть RxSwiftExt , вы можете просто использовать операторы errors() и elements():

stream.elements()
    .debug("elements")
    .subscribe()

stream.errors()
    .debug("errors")
    .subscribe()

Это обеспечит следующий вывод:

errors -> Event next(randomError)
elements -> Event next(hey4)
elements -> Event next(hey6)
elements -> Event next(hey8)

При использовании этой стратегии не забывайте добавлять share() после вашей flatMap, поэтому многие подписки не вызывают многократной обработки.

Вы можете узнать больше о том, почему вы должны использовать share в этой ситуации здесь: http://adamborek.com/how-to-handle-errors-in-rxswift/

Надеюсь, это поможет!

0 голосов
/ 22 сентября 2018

Да, это боль.Я думал об идее создания новой библиотеки, в которой грамматика не требует завершения потока при ошибке, но попытка воспроизвести всю экосистему Rx для нее кажется бессмысленной.

Существуют реактивные библиотеки, которые позволяют вам указать Never в качестве типа ошибки (что означает, что ошибка вообще не может быть выдана), а в RxCocoa вы можете использовать Driver (который не может содержать ошибку), ноу вас все еще остался весь танец Результата«Монады в моих монадах!».

Чтобы правильно с этим справиться, вам понадобится комплект Монадных трансформаторов .С их помощью вы можете делать все отображение / flatMapping, которое вы хотите, и не беспокоиться о поиске ошибок до самого конца.

...