RxSwift:嵌套查询和ReplaySubject

时间:2016-07-05 16:40:21

标签: swift reactive-programming rx-swift behaviorsubject

我必须使用三个单独的API请求来获取三种类型的数据(ATypeBTypeCType)。 API返回的对象由一对多关联:

  • 1 AType对象是N BType个对象
  • 的父对象
  • 1 BType对象是P CType个对象的父对象)

我使用以下三个函数来获取每种类型:

func get_A_objects() -> Observable<AType> { /* code here */ }
func get_B_objects(a_parentid:Int) -> Observable<BType> { /* code here */}
func get_C_objects(b_parentid:Int) -> Observable<CType> { /* code here */}

并且为了避免嵌套订阅,使用flatMap链接这三个函数:

func getAll() -> Observable<CType> {
  return self.get_A_objects()
     .flatMap { (aa:AType) in  return get_B_objects(aa.id) }
     .flatMap { (bb:BType) in  return get_C_objects(bb.id) }
}

func setup() {
  self.getAll().subscribeNext { _ in
    print ("One more item fetched") 
  }
}

上面的代码工作正常,当有AType的M个对象时,我可以看到文本"One more item fetched"打印了MxNxP次。

我想设置getAll()功能,使用ReplaySubject<String>在整个链中提供的状态更新。我最初的想法是写下这样的东西:

func getAll() -> ReplaySubject<String> {
  let msg = ReplaySubject<String>.createUnbounded()
  self.get_A_objects().doOnNext { aobj in msg.onNext ("Fetching A \(aobj)") }
    .flatMap { (aa:AType) in 
       return get_B_objects(aa.id).doOnNext { bobj in msg.onNext ("Fetching B \(bobj)") }
    }
    .flatMap { (bb:BType) in
       return get_C_objects(bb.id).doOnNext { cobj in msg.onNext ("Fetching C \(cobj)") }
    }

  return msg
}

但此尝试失败,即以下print()不会打印任何内容。

getAll().subscribeNext {
  print ($0)
}

我该如何重写逻辑?

1 个答案:

答案 0 :(得分:1)

问题

这是因为你没有保留你的Disposable,因此他们会立即被解除分配,因此什么都不做。

getAll中,您可以通过Observable<AType>创建get_A_objects(),但不会将其添加到DisposeBag。当它超出范围时(在func的末尾),它将被取消分配。所以{ aobj in msg.onNext ("Fetching A \(aobj)") }永远不会发生(或者至少不太可能,如果它是异步的)。

此外,您不会保留ReplaySubject<String>返回的getAll().subscribeNext。因此,出于同样的原因,这也将是一个交易破坏者。

解决方案

由于您需要两个Observable:一个用于实际的最终结果(Observable<CType>),另一个用于进度状态(ReplaySubject<String>),您应该从{{{ 1}}函数,这样两者都可以“拥有”,并且可以管理它们的生命周期。

getAll()

一些注意事项:

  • 您不应该使用func getAll() -> (Observable<CType>, ReplaySubject<String>) { let progress = ReplaySubject<String>.createUnbounded() let results = self.get_A_objects()...... return (results, progress) } let (results, progress) = getAll() progress .subscribeNext { print ($0) } .addDisposableTo(disposeBag) results .subscribeNext { print ($0) } .addDisposableTo(disposeBag) ,如果您不小心可能会有危险。
  • 你可能根本不想真正使用createUnbounded,因为如果有人在之后订阅并且获得旧的进度状态消息,那么你以后会“抓取”某些内容将会是一个谎言。考虑使用ReplaySubject
  • 如果您遵循上述建议,那么您只需要确保在PublishSubject之前订阅progress以确保您不会错过任何进度状态消息,因为输出已赢得不再缓冲了。
  • 另外,仅仅是我的意见,但我会重新将“获取XY”改为其他内容,因为你没有“抓取”,但你已经已经“获取 ed < / strong>“它。