问题描述
我知道使用 Rx 的 flatmap
或 flatmapLatest
比嵌套订阅更可取。但是,我找不到“应该不惜一切代价避免嵌套订阅调用”的令人信服的理由 (RxSwift Github Tips),并想了解原因。
除了“糟糕的代码异味”之外,还有对具体问题的了解吗?
示例 (source):
使用嵌套订阅(不好)
textField.rx.text.subscribe(onNext: { text in
performURLRequest(text).subscribe(onNext: { result in
...
})
.disposed(by: disposeBag)
})
.disposed(by: disposeBag)
使用 flatmapLatest(好)
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
解决方法
在这种特殊情况下,坏示例可能会产生过时的结果 - 当 performURLRequest
更新并触发新查询时,没有什么可以阻止对 textField
产生过时的响应。>
当然,这取决于您的用例,但显示基于过时值 textField
的结果(例如搜索结果)通常是错误的。更糟糕的是,可能发生较早的 URLRequest 运行缓慢并在之后返回,从而无限期地显示错误结果。
相比之下,flatMapLatest
确保在 textField 更新后立即取消订阅来自先前值的待处理结果流,从而防止处理过时的结果。
这个并发问题是使用单个流通常可以实现更好的协调和效率的一个例子。
它还使订阅管理更加清晰,并降低您无法正确清理的可能性。
,我认为首先要注意的是您的两个示例没有相同的行为。第一个将在每次文本字段更改时发出网络请求而不取消前一个请求。在后一种情况下,当文本更改时,先前的请求(如果有)将被取消并开始新的请求。
如果后一个示例使用 flatMap 而不是 flatMapLatest,它们会更相似,但请注意,在前一种情况下,这是 url 请求可以组合的唯一方式。在后一种情况下,您可以使用 flatMapLatest 来确保取消旧请求(或 flatMapFirst 在请求进行中时忽略事件,或 concatMap 存储事件直到前一个请求完成。)使用 flatMap更加灵活。
就我个人而言,我确实在一些选定的情况下嵌套订阅。例如,当我要忽略内部订阅的事件(甚至不捕获其 Disposable)或设置异步循环构造时。