简体   繁体   中英

RxSwift: Why does flatMapLatest never execute onCompleted()?

In my Swift UIViewController, I'm attempting to subscribe to a class member of type Variable, run it through a flatMapLatest call, and then have the onCompleted() call in the flatMapLatest observable execute on all subscribers. However, while onNext() is called, onCompleted() never is and I'm not sure why.

My class member is defined as:

private let privateVar = Variable<String>("")

while in my viewDidLoad() method, I set up the observables:

let localVar = self.privateVar.asObservable().distinctUntilChanged()

localVar.subscribe(onNext: { [weak self] sent in print("first onNext called") })
        .disposed(by: self.disposeBag)

let mappedVar = localVar.flatMapLatest { self.ajaxLoad(var1: $0) }.share()

mappedVar.subscribe(
  onNext: { [weak self] queryRes in
    print("onNext called!")
  },
  onCompleted: { [weak self] in
    print("onCompleted called!")
  }
)
.disposed(by: self.disposeBag)

and my ajaxLoad method:

func ajaxLoad(var1 myVar: String) -> Observable<QueryResponse> {
  return Observable.create { observable in
    apollo.fetch(query: MyQuery()) { (result, _) in
      observable.onNext(result?.data?.myQuery)
      observable.onCompleted()
    }

    return Disposables.create()
  }
}

I'm fairly new to ReactiveX so I may be a little hazy on what the Rx lifecycle actually looks like. Why might onNext be called in the flatMapLatest call, but not onCompleted? Any help would be appreciated. Thanks in advance!

The flatMap operator does not emit completed events of any observable that you return inside the block.

The following code illustrates this clearly. .just(_) emits the element and then a completed event, which does not terminate to subscription.

_ = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
    .debug("before flatmap")
    .flatMap { .just($0 * 2) }
    .debug("after flatmap")
    .subscribe()

In fact, Variable only emits completed when deallocated. See source v4.0 . Note that Variable is deprecated in RxSwift 4, you are encouraged to use RxCocoa's similar BehaviorRelay instead.

deinit {
    _subject.on(.completed)
}

Since you said you are new and a "little hazy"...

Keep in mind that whenever localVar changes, it emits a new value and ajaxLoad(var1:) gets called. The result of ajaxLoad(var1:) then gets pushed to your subscribe's onNext closure.

Also keep in mind that if an Observable emits a .completed it's dead. It can no longer emit anything else.

So flatMapLatest can't complete (unless its source completes.) If it did, it would kill the whole pipe and no more changes to localVar would get routed through the pipe, ajaxLoad(var:1) wouldn't get called again with the new value and nothing more would get pushed to the subscribe's onNext method.

A sequence of observables can be thought of like a Rube Goldberg machine where a completed shuts down the machine and an error breaks it. The only time you should shut down the machine is if the source (in this case localVar ) is finished emitting values or the sink (destination, in this case the onNext: closure) doesn't want any more values.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM