我有一个由多个运算符组成的序列。在此序列处理过程中,总共有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运算符如何捕获所有错误并在最后发出失败结果,但不将整个序列置于第一个错误上?
答案 0 :(得分:3)
想到的第一个技巧是使用materialize
。这会将每个Observable<T>
转换为Observable<Event<T>>
,因此错误将只是.next(.error(Error))
,不会导致序列终止。
materialize
放置该特定片段。之所以需要这样做是因为物化序列仍然可以完成,这在规则链的情况下会导致终止,但不会终止flatMapped链(因为flatMap内的complete ==成功完成)。
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)
现在您要做的就是仅过滤实例化序列中的“ next”事件。
如果您有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
,因此许多订阅不会引起多处处理。
您可以在此处http://adamborek.com/how-to-handle-errors-in-rxswift/
了解更多有关为什么在这种情况下应该使用共享的信息。希望这会有所帮助!
答案 1 :(得分:1)
是的,这很痛苦。我考虑过创建一个新的库的想法,该库不需要语法以错误结束流,但是尝试重现整个Rx生态系统似乎毫无意义。
有些反应式库允许您将Never
指定为错误类型(这意味着根本不会发出错误),在RxCocoa中,您可以使用Driver(不会出错),但是您可以仍然留下整个结果舞蹈。 “我的Monad中的Monad!”。
要正确处理,您需要一组Monad transformers。有了这些,您就可以完成所需的所有映射/平面映射,而不必担心直到最后都将看到错误。