2017-01-16 292 views
1

我目前在使用RxSwift Observables时执行多个网络请求时会出现问题。我明白,如果一个人创建了一个冷观察者,并且它有多个观察者,那么观察者将在每次订阅时执行它的块。RxSwift:防止多个网络请求

我试图创建一次执行网络请求的共享订阅observable,并将多个订户通知结果。以下是我所尝试的。事件

  1. 序列与一个UIButton

  2. 的点击事件创建视图模型创建serviceStatus观察为上视图模型公共财产。这个Observable从buttonTapped Observable映射而来。然后过滤掉“加载”状态。返回的Observable在其上执行shareReplay(1)以返回共享订阅。
  3. 在视图模型上创建serviceExecuting Observable作为公共属性。这个可观察值是从serviceStatus Observable映射而来的。如果状态是“加载”
  4. 绑定的UILabel到serviceStatus可观察
  5. 绑定活动的指标为serviceExecuting可观察到它会返回true。

当按钮被点击时,服务请求被执行三次,我期待它只被执行一次。有什么事情脱颖而出吗?

代码

class ViewController { 

    let disposeBag = DisposeBag() 
    var button: UIButton! 
    var resultLabel: UILabel! 
    var activityIndicator: UIActivityIndicator! 

    lazy var viewModel = { // 1 
     return ViewModel(buttonTapped: self.button.rx.tap.asObservable()) 
    } 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     self.viewModel.serviceStatus.bindTo(self.resultLabel.rx_text).addDispsoableTo(disposeBag) // 4 
     self.viewModel.serviceExecuting.bindTo(self.activityIndicator.rx_animating).addDispsoableTo(disposeBag) // 5 
    } 
} 

class ViewModel { 

    public var serviceStatus: Observable<String> { // 2 
     let serviceStatusObseravble = self.getServiceStatusObservable() 
     let filtered = serviceStatusObseravble.filter { status in 
      return status != "Loading" 
     } 
     return filtered 
    } 

    public var serviceExecuting: Observable<Bool> { // 3 
     return self.serviceStatus.map { status in 
      return status == "Loading" 
     } 
     .startWith(false) 
    } 

    private let buttonTapped: Observable<Void> 

    init(buttonTapped: Observable<Void>) { 
     self.buttonTapped = buttonTapped 
    } 

    private func getServiceStatusObservable() -> Observable<String> { 
     return self.buttonTapped.flatMap { _ -> Observable<String> in 
      return self.createServiceStatusObservable() 
     } 
    } 

    private func createServiceStatusObservable() -> Observable<String> { 
     return Observable.create({ (observer) -> Disposable in 

     someAsyncServiceRequest() { result } 
      observer.onNext(result) 
     }) 

     return NopDisposable.instance 
    }) 
    .startWith("Loading") 
    .shareReplay(1) 
} 

编辑:

基于下面的对话,下面就是我一直在寻找...

我需要申请份额( )函数返回从getServiceStatusObservable()方法返回的Observable,而不是从createServiceStatusObservable()方法返回的Observable。有多名观察员被添加到这个观察者来检查当前状态。这意味着执行网络请求的observable被执行了N次(N是观察者的数量)。现在每次点击按钮时,网络请求都会执行一次,这正是我所需要的。

private func getServiceStatusObservable() -> Observable<String> { 
    return self.buttonTapped.flatMap { _ -> Observable<String> in 
     return self.createServiceStatusObservable() 
    }.share() 
} 

回答

4

.shareReplay(1)将只适用于observable的一个实例。在createServiceStatusObservable()中创建它时,共享行为只会影响此函数返回的一个值。

class ViewModel { 
    let serviceStatusObservable: Observable<String> 

    init(buttonTapped: Observable<Void>) { 
    self.buttonTapped = buttonTapped 
    self.serviceStatusObservable = Observable.create({ (observer) -> Disposable in 
     someAsyncServiceRequest() { result in 
      observer.onNext(result) 
     } 

     return NopDisposable.instance 
    }) 
    .startWith("Loading") 
    .shareReplay(1) 
    } 

    private func getServiceStatusObservable() -> Observable<String> { 
    return self.buttonTapped.flatMap { [weak self] _ -> Observable<String> in 
     return self.serviceStatusObservable 
    } 
    } 
} 

在这个版本中,serviceStatusObservable仅创建一次,因此它的副作用将共享每次它被使用,因为它是相同的实例

+0

这是一个很好的地方,它看起来像解决了第一次点击按钮的问题。我更新shareReplay()to share(),因为我不希望缓存结果,因为用户可以多次点击该按钮来执行新的服务请求。这是否意味着我应该对所有其他可观察的事件做同样的事情,因为按钮的后续点击会导致对该服务的多个请求。 –

+0

只要在第一次订阅取消订阅之前完成对其他观察对象的订阅,就可以仅在网络请求上保留“共享”。 (在这种情况下,将执行新的请求)。 'share()'记录在[这里](https://github.com/ReactiveX/RxSwift/blob/3c55a309a24fbe3dbc480431798eec072b633b85/RxSwift/Observables/Observable%2BBinding.swift#L136)。 – tomahh

+0

哎呀。我猜想我在这里对getServiceStatusObservable()方法感到困惑。点击按钮时,已注册的块被调用三次。 {[weak self] _ - > Observable in return self.serviceStatusObservable } –