首页 > 解决方案 > iOS RxSwift 如何防止序列被处理(抛出错误)?

问题描述

我有一个由多个运算符组成的序列。在这个序列处理过程中,总共有 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只会捕获错误,允许我将其转换为失败结果,但会阻止序列被释放。但是,我看到第一次发现错误时,整个序列都被释放,不再有事件通过。

如果没有这种行为,我会留下一个令人毛骨悚然的 Results<> ,并且必须多次分支我的序列以将其定向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 运算符如何捕获所有错误并在最后发出失败结果,但不处理第一个错误时的整个序列?

标签: ioserror-handlingobservablerx-swiftonerror

解决方案


想到的第一个技巧是使用materialize. 这会将 every 转换Observable<T>Observable<Event<T>>,因此 Error 只是 a.next(.error(Error))并且不会导致序列终止。

但是,在这种特定情况下,将需要另一个技巧。将你的整个“触发”链也放在一个 flatMap 中,并materializeing 那个特定的部分。这是必需的,因为物化序列仍然可以完成,这会在常规链的情况下导致终止,但不会终止 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()andelements()运算符:

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()在 your 之后添加flatMap,如此多的订阅不会导致多块处理。

你可以在这里阅读更多关于为什么你应该在这种情况下使用共享的信息:http: //adamborek.com/how-to-handle-errors-in-rxswift/

希望这可以帮助!


推荐阅读