Reputation: 31
I am aware that using Rx's flatmap
or flatmapLatest
is preferable to having nested subscriptions. However, I can't find a compelling reason why nested subscription calls "should be avoided at all costs" (RxSwift Github Tips), and want to understand why.
Any insight into the specific issue(s) besides "bad code smell"?
Example (source):
Using nested subscription (bad)
textField.rx.text.subscribe(onNext: { text in
performURLRequest(text).subscribe(onNext: { result in
...
})
.disposed(by: disposeBag)
})
.disposed(by: disposeBag)
Using flatmapLatest (good)
textField.rx.text
.flatMapLatest { text in
// Assuming this doesn't fail and returns result on main scheduler,
// otherwise `catchError` and `observeOn(MainScheduler.instance)` can be used to
// correct this.
return performURLRequest(text)
}
...
.disposed(by: disposeBag) // only one top most disposable
Upvotes: 2
Views: 390
Reputation: 29776
In this particular case, the bad example can yield out of date results - there is nothing that prevents an outdated response to performURLRequest
being yielded when the textField
updates and triggers a new query.
Of course it depends on your use case, but its often a mistake to display a result (such as a search result) based on an outdated value of textField
. Worse, it could happen that an earlier URLRequest runs slowly and returns after a later one, leaving an incorrect result displayed indefinitely.
In contrast flatMapLatest
ensures that the pending result stream from the previous value is unsubscribed as soon the textField is updated, which prevents outdated results from being processed.
This concurrency issue is one example of the better coordination and efficiency often achieved by using a single stream.
It also makes subscription management clearer and makes it less likely that you will fail to clean up properly.
Upvotes: 2
Reputation: 33967
I think the first thing to notice is that your two examples don't have the same behavior. The first one will make a network request every time the text field changes without canceling the previous request. In the latter case, when the text changes, the previous request (if any) is cancelled and a new request is started.
If the latter example used flatMap instead of flatMapLatest, they would have been more alike but notice that is the only way the url requests can be combined in the former case. In the latter case you can use flatMapLatest to ensure cancelation of the old request (or flatMapFirst to ignore events when a request is in-flight, or concatMap to store events until the previous request completed.) Using flatMap is much more flexible.
Personally, I do nest subscribes in a few select cases. For example when I'm going to ignore the inner subscribe's events (not even capturing its Disposable) or if setting up an asynchronous looping construct.
Upvotes: 0