Managing errors
在应用程序中比较常见的 error 有:
- No Internet connecting: 无网络连接
- Invalid input: 无效输入
- API error or HTTP error: 请求结果返回错误
对于所有接受闭包的操作符,RxSwift 将闭包内抛出的任何错误转化为终止可观察的错误事件。这个错误事件是你可以捕捉并采取行动的。它可以用两种方式处理:
[图片上传失败...(image-dd6875-1639497431330)]
- Catch: 用一个默认值来从错误中恢复
[图片上传失败...(image-2b2be8-1639497431330)]
- Retry: 有限次数或者无限次数重试
Throwing errors
在 URLSession+Rx.swift 文件中,可以看到:
public func data(request: URLRequest) -> Observable {
return self.response(request: request).map { pair -> Data in
if 200 ..< 300 ~= pair.0.statusCode {
return pair.1
} else {
throw RxCocoaURLError.httpRequestFailed(response: pair.0, data: pair.1)
}
}
}
这短短六行向我们展示了如何抛出一个错误,具体来说,就是一个定制的错误,这个后面会学到。
注意,在这个闭包中没有 return error,用了 throw 就不需要再使用 return。
Handle errors with catch
最基础的方法是使用 catch
, catch
操作符使用起来就好像 Swift 中的 do-try-catch
。
在 RxSwift 中主要有两种操作符用来 catch errors, 第一个:
func catchError(_ handler:) -> RxSwift.Observable
这是一个通用的操作符;它接受一个闭包作为参数,并提供了返回一个完全不同的可观察变量的机会。如果你不太明白在哪里使用这个选项,可以考虑一个缓存策略,如果观测器出错,就返回一个先前缓存的值。通过这个操作符,你可以实现以下流程:
[图片上传失败...(image-1f60e8-1639497431330)]
在这种情况下,catchError
返回以前可用的值,但由于某种原因,这些值不再可用了。
第二个操作符是:
func catchErrorJustReturn(_ element:) -> RxSwift.Observable
它忽略了错误,只是返回一个预先定义的值。这个操作符比前一个操作符更有局限性,因为它不可能为给定的错误类型返回一个值--对于任何错误都返回相同的值,无论错误是什么。
A common pitfall (一个常见的陷阱)
当一个 Observable error out,error subscription 会被通知,然后所有的订阅都被处理掉。
因此,当一个 Observable error out 时,观察者基本上就被终止了,并且错误之后的任何事件都将被忽略。这是观察者契约的一个规则。
在之前的例子中,如果搜索天气时,抛出了一个 404 error,会发现后面的搜索全部停止工作了,这其实是一个不好的体验。
Catching errors
private var cache = [String: Weather]()
let textSearch = searchInput.flatMap { text in
return ApiController.shared.currentWeather(city: text)
.do(onNext: { [weak self] data in
self?.cache[text] = data
})
.catchError { error in
return Observable.just(self?.cache[text] ?? .empty)
}
}
Retrying on errors
Catching error 只是 RxSwift 中处理错误的一种方式。你也可以用 retry 来处理错误。当使用 retry
操作符时,Observable 出错,Observable 将重复自己。重要的是要记住,重试意味着重复 Observable 内的整个任务。
这也是建议避免在 Observable 内改变用户界面的副作用的主要原因之一,因为你无法控制谁会重试它!
Retry operators
有三种类型的 retry 操作符,第一个:
func retry() -> Observable
第二个:
func retry(_ maxAttemptCount:) -> Observable
栗子:
let textSearch = searchInput.flatMap { text in
return ApiController.shared.currentWeather(city: text)
.do(onNext: { [weak self] data in
self?.cache[text] = data
})
.retry(3)
.catchError { [weak self] error in
return Observable.just(self?.cache[text] ?? .empty)
}
}
如果 Observable 产生错误,它将被连续重试最多三次,即最初的尝试和另外两次尝试。如果它第四次出错,这个错误将不会被处理,执行将转到 catchError 操作符。
Advanced retries
最后一个操作符是 retryWhen
, 适用于高级重试情况。这个错误处理操作符被认为是最强大的一个。
func retryWhen(_ notificationHandler:) -> Observable
这里我们需要用到 retryWhen 操作符,这个操作符主要描述应该在何时重试,并且通过闭包里面返回的 Observable
来控制重试的时机。
Creating custom errors
enum ApiError: Error {
case cityNotFound
case serverFailure
}