Managing errors
在應(yīng)用程序中比較常見(jiàn)的 error 有:
- No Internet connecting: 無(wú)網(wǎng)絡(luò)連接
- Invalid input: 無(wú)效輸入
- API error or HTTP error: 請(qǐng)求結(jié)果返回錯(cuò)誤
對(duì)于所有接受閉包的操作符,RxSwift 將閉包內(nèi)拋出的任何錯(cuò)誤轉(zhuǎn)化為終止可觀察的錯(cuò)誤事件。這個(gè)錯(cuò)誤事件是你可以捕捉并采取行動(dòng)的父能。它可以用兩種方式處理:
[圖片上傳失敗...(image-dd6875-1639497431330)]
- Catch: 用一個(gè)默認(rèn)值來(lái)從錯(cuò)誤中恢復(fù)
[圖片上傳失敗...(image-2b2be8-1639497431330)]
- Retry: 有限次數(shù)或者無(wú)限次數(shù)重試
Throwing errors
在 URLSession+Rx.swift 文件中皂冰,可以看到:
public func data(request: URLRequest) -> Observable<Data> {
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)
}
}
}
這短短六行向我們展示了如何拋出一個(gè)錯(cuò)誤栓票,具體來(lái)說(shuō)舞丛,就是一個(gè)定制的錯(cuò)誤构捡,這個(gè)后面會(huì)學(xué)到独郎。
注意踩麦,在這個(gè)閉包中沒(méi)有 return error,用了 throw 就不需要再使用 return氓癌。
Handle errors with catch
最基礎(chǔ)的方法是使用 catch
, catch
操作符使用起來(lái)就好像 Swift 中的 do-try-catch
谓谦。
在 RxSwift 中主要有兩種操作符用來(lái) catch errors, 第一個(gè):
func catchError(_ handler:) -> RxSwift.Observable<Self.E>
這是一個(gè)通用的操作符;它接受一個(gè)閉包作為參數(shù)贪婉,并提供了返回一個(gè)完全不同的可觀察變量的機(jī)會(huì)反粥。如果你不太明白在哪里使用這個(gè)選項(xiàng),可以考慮一個(gè)緩存策略,如果觀測(cè)器出錯(cuò)才顿,就返回一個(gè)先前緩存的值莫湘。通過(guò)這個(gè)操作符,你可以實(shí)現(xiàn)以下流程:
[圖片上傳失敗...(image-1f60e8-1639497431330)]
在這種情況下娜膘,catchError
返回以前可用的值逊脯,但由于某種原因,這些值不再可用了竣贪。
第二個(gè)操作符是:
func catchErrorJustReturn(_ element:) -> RxSwift.Observable<Self.E>
它忽略了錯(cuò)誤军洼,只是返回一個(gè)預(yù)先定義的值。這個(gè)操作符比前一個(gè)操作符更有局限性演怎,因?yàn)樗豢赡転榻o定的錯(cuò)誤類(lèi)型返回一個(gè)值--對(duì)于任何錯(cuò)誤都返回相同的值匕争,無(wú)論錯(cuò)誤是什么。
A common pitfall (一個(gè)常見(jiàn)的陷阱)
當(dāng)一個(gè) Observable error out爷耀,error subscription 會(huì)被通知甘桑,然后所有的訂閱都被處理掉。
因此歹叮,當(dāng)一個(gè) Observable error out 時(shí)跑杭,觀察者基本上就被終止了,并且錯(cuò)誤之后的任何事件都將被忽略咆耿。這是觀察者契約的一個(gè)規(guī)則德谅。
在之前的例子中,如果搜索天氣時(shí)萨螺,拋出了一個(gè) 404 error窄做,會(huì)發(fā)現(xiàn)后面的搜索全部停止工作了,這其實(shí)是一個(gè)不好的體驗(yàn)慰技。
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 中處理錯(cuò)誤的一種方式椭盏。你也可以用 retry 來(lái)處理錯(cuò)誤。當(dāng)使用 retry
操作符時(shí)吻商,Observable 出錯(cuò)掏颊,Observable 將重復(fù)自己。重要的是要記住艾帐,重試意味著重復(fù) Observable 內(nèi)的整個(gè)任務(wù)蚯舱。
這也是建議避免在 Observable 內(nèi)改變用戶界面的副作用的主要原因之一,因?yàn)槟銦o(wú)法控制誰(shuí)會(huì)重試它掩蛤!
Retry operators
有三種類(lèi)型的 retry 操作符枉昏,第一個(gè):
func retry() -> Observable<Element>
第二個(gè):
func retry(_ maxAttemptCount:) -> Observable<Element>
栗子:
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 產(chǎn)生錯(cuò)誤,它將被連續(xù)重試最多三次揍鸟,即最初的嘗試和另外兩次嘗試兄裂。如果它第四次出錯(cuò)句旱,這個(gè)錯(cuò)誤將不會(huì)被處理,執(zhí)行將轉(zhuǎn)到 catchError 操作符晰奖。
Advanced retries
最后一個(gè)操作符是 retryWhen
, 適用于高級(jí)重試情況谈撒。這個(gè)錯(cuò)誤處理操作符被認(rèn)為是最強(qiáng)大的一個(gè)。
func retryWhen(_ notificationHandler:) -> Observable<Element>
這里我們需要用到 retryWhen 操作符匾南,這個(gè)操作符主要描述應(yīng)該在何時(shí)重試啃匿,并且通過(guò)閉包里面返回的 Observable
來(lái)控制重試的時(shí)機(jī)。
Creating custom errors
enum ApiError: Error {
case cityNotFound
case serverFailure
}