RxSwift 有着优秀的处理错误的方案,但在讨论这个问题之前,我们不如先来思考下为什么在 RxSwift 中有 catchError
操作符。
我们先来想象几个场景:
这些都属于出现错误的场景。
但在讨论上述问题前,我们先来思考一下在 RxSwift 中,出现 error 会发生什么事情。
来看下面这段代码:
Observable<Int>.from([1, 2, 3, -1, 5])
.map { value -> Int in
if value > 0 {
return value
} else {
throw CustomError.myCustom
}
}
.debug()
.subscribe()
.disposed(by: disposeBag)
输出结果为:
-> subscribed
-> Event next(1)
-> Event next(2)
-> Event next(3)
-> Event error(myCustom)
-> isDisposed
我们可以清晰地看到在发射时到变换再到订阅的过程中,我们发射了一个 -1
,在 map
过程中抛出了错误,导致我们收到 1 、 2 、 3 后只收到了一个 error myCustom
。
但这个场景一般不适合直接应用到开发中。举个例子,当我们点击 Button 时,进行一个网络请求,网络请求出现错误,抛出 error ,展示错误信息。整个事件结束了,当我们再次点击该 Button 时,则不会再触发网络请求。
类似上述代码,5
没有被订阅者接收到。
当 Observer 收到 Observable 一个错误时,说明该 Observable 不能正常传递值了, Observer 不再对该 Observable 感兴趣,此时 Observer 取消订阅了该 Observable 。Observable 再去传递任何值 Observer 也不知道了。
为此,我们需要 catch 这个网络请求的错误,保证一切在正常的运行。而 catch 后做什么也是一件重要的事情,比如我们可以选择重试、用一个默认值替换或者阻止本次事件继续向下发射。
我们来 catch 上述发射数字的代码:
Observable<Int>.from([1, 2, 3, -1, 5])
.map { value -> Int in
if value > 0 {
return value
} else {
throw CustomError.myCustom
}
}
.catchError { (error) -> Observable<Int> in
return Observable.just(1)
}
.debug()
.subscribe()
.addDisposableTo(disposeBag)
结果如下:
-> subscribed
-> Event next(1)
-> Event next(2)
-> Event next(3)
-> Event next(1)
-> Event completed
-> isDisposed
Observer
没有收到 error ,在前面触发 error 时,我们通过调用方法 catchError 将出现错误的 Observable
替换成了一个新的 Observable
,这里是 Observable.just(1)
。
这可能并不是我们想要的,如果我们还想接收到这个 5 怎么办?
我们需要先明确一个概念:
除非通过重新订阅,否则在一个 Observable 发射 error 后,你不再能接收到任何它发射的任何值。
上述代码中:
Observable<Int>.from([1, 2, 3, -1, 5])
.map { value -> Int in
if value > 0 {
return value
} else {
throw CustomError.myCustom
}
}
这个Observable已经发射了CustomError.myCustom
,我们无论如何也收不到5这个值。
但稍加修改,我们就可以收到5这个值:
Observable<Int>.from([1, 2, 3, -1, 5])
.flatMap { value -> Observable<Int> in
Observable.just(value)
.map { value -> Int in
if value > 0 {
return value
} else {
throw CustomError.myCustom
}
}
.catchErrorJustReturn(1)
}
.debug()
.subscribe()
.addDisposableTo(disposeBag)
输出结果:
-> subscribed
-> Event next(1)
-> Event next(2)
-> Event next(3)
-> Event next(1)
-> Event next(5)
-> Event completed
-> isDisposed
原因在于我们 catch 的 Observable 是:
Observable.just(value)
.map { value -> Int in
if value > 0 {
return value
} else {
throw CustomError.myCustom
}
}
当这一 Observabl e出现错误时,替换成 Observable.just(1)
。
这样一来,在 flatMap
后的 Observable
变成了正常的 Observable
,我们可以继续变换值,自然最终我们可以接收到5。
我们还可以通过返回一个 Observable.empty()
忽略出现错误的值:
Observable<Int>.from([1, 2, 3, -1, 5])
.flatMap { value -> Observable<Int> in
Observable.just(value)
.map { value -> Int in
if value > 0 {
return value
} else {
throw CustomError.myCustom
}
}
.catchError { (error) -> Observable<Int> in
return Observable.empty()
}
}
.debug()
.subscribe()
.addDisposableTo(disposeBag)
此时输出结果为:
-> subscribed
-> Event next(1)
-> Event next(2)
-> Event next(3)
-> Event next(5)
-> Event complete
切记:在使用 catchError 时,我们要时刻明确我们 catch 的是哪个 Observable ,并将这个 Observable 替换成了什么 Observable 。
接下来我们将讨论如何创建一些定制的错误处理方法,以及如何在实际项目中处理错误的时机与方案。