RxSwift

来源:互联网 发布:修真的程序员txt下载 编辑:程序博客网 时间:2024/05/16 17:07
首先我们一起重新回顾一下,什么时候观察者序列会终止?

非常简单的几句话:无论什么时候观察者序列接收到错误(error)或者完成(completed)事件,观察者序列(Observables)将终止。终止意味着观察者序列的订阅者们将不再接收任何新的消息。

在开发中,我们经常碰到需要处理错误的情况,现在我们看一个真实的场景,比如:当用户点击按钮的时候,app经常需要进行一些API请求。如果我们请求操作使得观察者序列出现了非预期的终止,那么后续的点击操作将无效,点击按钮事件不再被发送。那么问题就变的麻烦起来了,接下来看看怎么处理这个情况,原文内容这里

功能描述:
现在我们来模拟当用户点击按钮的时候,app进行API请求。点击success按钮,将调用假的API请求,成功返回数据。同理,当点击failure按钮时,调用假的API请求,返回错误。并且每次操作按钮,记录成功和失败的次数。

UI布局:


具体实现代码:

let successesCount = Observable            .of(successButton.rx.tap.map { true }, failureButton.rx.tap.map { false })            .merge()             .flatMap { [unowned self] performWithSuccess in                return self.performAPICall(shouldEndWithSuccess: performWithSuccess)             }.scan(0) { accumulator, _ in                return accumulator + 1              }.map { "\($0)" }        successesCount.bindTo(successessCountLabel.rx.text)            .disposed(by: disposeBag)
我们可以看到上面代码使用了of操作符创建了一个新的观察者序列,并且调用merge()合并successButton和failureButton两个按钮点击事件观察者序列,然后执行flatMap,进行API请求。每次请求,调用scan操作符记录操作的次数并调用map转换为字符串。下面是performAPICall

struct SampleError: Swift.Error {}   private func performAPICall(shouldEndWithSuccess: Bool) -> Observable<Void> {        if shouldEndWithSuccess {            return .just(())        } else {            return .error(SampleError())        }    }

我们可能会想,上面的代码很简单啊。那么结果如何呢?下面看一下具体操作,当点击successButton,的确增加了成功的次数,但是,只要我们点击failureButton,整个观察者序列将被终止并dispose掉。而且再次点击successButton无效。并不会看到有任何改变,如下所示:



那么为什么会这样呢?其实在最开始,我们就一起回顾了观察者序列终止的情况:无论什么时候观察者序列接收到错误(error)或者完成(completed)事件,观察者序列(Observables)将终止。终止意味着观察者序列的订阅者们将不再接收任何新的消息。所以,如果一旦点击了failureButton,那么调用performAPICall方法的时候,会发送错误事件,那么观察者序列接收到了错误事件,自然而然就终止了。

但是实际情况是,即使接收了错误的情况,但是我们并不想终止后续的操作。所以我们可能想API调用返回的结果应该是Observable<Result<T>>,使用Result<T>做为下一个事件,这样就不会终止观察者序列了。这里有一个简单的方式处理,一个第三方库RxSwiftExt通过了materialize操作符。该操作符会返回一个观察者序列包含对应的事件,即转换Observable<T>到Observable<Event<T>>如下:

/**    Convert any Observable into an Observable of its events.        - seealso: [materialize operator on reactivex.io](http://reactivex.io/documentation/operators/materialize-dematerialize.html)     - returns: An observable sequence that wraps events in an Event<E>. The returned Observable never errors, but it does complete after observing all of the events of the underlying Observable.         */   public func materialize() -> RxSwift.Observable<RxSwift.Event<Self.E>>
Event就是RxSwift的Event枚举

/// Represents a sequence event.////// Sequence grammar: /// **next\* (error | completed)**public enum Event<Element> {    /// Next element is produced.    case next(Element)    /// Sequence terminated with an error.    case error(Swift.Error)    /// Sequence completed successfully.    case completed}
并且有两个额外的操作符号,能够获取对应事件的数据

    /// Return only the error events of an Observable<Event<E>>    public func errors() -> RxSwift.Observable<Error>    /// Return only the onNext element events of an Observable<Event<E>>    public func elements() -> RxSwift.Observable<Self.E.E>
由于上述功能,现在我们可能很好的处理之前的问题了,实现如下:

 private func usingMaterialize() {        let result = Observable            .of(successButton.rx.tap.map { true }, failureButton.rx.tap.map { false })            .merge()            .flatMap { [unowned self] performWithSuccess in                return self.performAPICall(shouldEndWithSuccess: performWithSuccess)                    .materialize()            }.share()        result.elements()            .scan(0) { accumulator, _ in                return accumulator + 1            }.map { "\($0)" }            .bindTo(successessCountLabel.rx.text)            .disposed(by: disposeBag)        result.errors()            .scan(0) { accumulator, _ in                return accumulator + 1            }.map { "\($0)" }            .bindTo(failuresCountLabel.rx.text)            .disposed(by: disposeBag)    }
注意:上面代码我们不仅使用了materialize而且使用了share(),保证共享同一观察者序列,避免每次有新的订阅执行不必要的API调用操作。具体原因可以看这里。之后我们就能够正常操作了。如下:



上面就是处理异常错误的方式。

推荐:

What is the correct way to handle errors