RxSwift #06 | Error handing

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
}

你可能感兴趣的:(RxSwift #06 | Error handing)