在 RxSwift 中重新启动可观察间隔的正确方法

Posted

技术标签:

【中文标题】在 RxSwift 中重新启动可观察间隔的正确方法【英文标题】:Correct way to restart observable interval in RxSwift 【发布时间】:2016-05-25 20:04:15 【问题描述】:

在我的 OS X 状态栏应用程序中,我使用interval 函数定期调用外部 api 并显示结果:

Observable<Int>
    .interval(120.0, scheduler: MainScheduler.instance)
    .startWith(-1) // to start immediately
    .flatMapLatest(makeRequest) // makeRequest is (dummy: Int) -> Observable<SummaryResponse?>
    .subscribeNext(setSummary)
    .addDisposableTo(disposeBag)

但是,如果用户同时更改了首选项,我想“重新启动”此间隔并立即拨打新电话以反映更改(无需等待下一次电话)。

最好的方法是什么?

    将 observable 存储为属性并将其设置为 nil 或在其上调用 .dispose()(或两者)并创建一个新的 observable ? 将disposeBag 设置为nil 并创建一个新的可观察对象? 还有其他方法吗?

【问题讨论】:

【参考方案1】:

您正在寻找的是merge。您有两个Observables,其中一个是interval,另一个代表偏好更改。您想将 merge 与两者的元素合并为一个 Observable,当它们出现时立即。

看起来像这样:

// this should really come from somewhere else in your app
let preferencesChanged = PublishSubject<Void>()

// the `map` is so that the element type goes from `Int` to `Void`
// since the `merge` requires that the element types match
let timer = Observable<Int>.timer(0, period: 3, scheduler: MainScheduler.instance).map  _ in () 

Observable.of(timer, preferencesChanged)
    .merge()
    .flatMapLatest(makeRequest)
    .subscribeNext(setSummary)
    .addDisposableTo(disposeBag)

还要注意我是如何使用timer 而不是interval,因为它允许我们指定第一次触发的时间,以及后续触发的时间段。这样,您就不需要startWith。然而,这两种方式都有效。这是一个偏好问题。

还有一点需要注意。这超出了您的问题范围(也许您为了这个问题而保持简单)但是您应该考虑将结果保留为 Observable 而不是 subscribeNext(setSummary),而不是 bindTodrive UI或 DB(或任何“摘要”)。

【讨论】:

感谢您的回答和所有建议!更改为 timer 并将虚拟 int 映射到 void 使这段代码看起来好多了:)。 setSummary 基本上设置NSStatusItem.title,会检查我是否可以使用Rx 来处理它:)。 但这并没有真正重置任何东西,是吗?它只是在计时器准备好时发出事件,或者其他可观察到的东西发出一些东西,对吗?如果这是真的,我仍然会对真正重置间隔的好方法感兴趣。 我不明白你的意思。我们不会重置计时器 Observable。相反,该计时器 Observable 被用于重置 makeRequest 返回的 Observable。听起来您想要的不是这个问题所要求的。

以上是关于在 RxSwift 中重新启动可观察间隔的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

在可观察对象中处理一次性物品的正确方法

RxSwift 可观察到的错误停止链 - 带有 Rx 的 Web 服务,如何恢复?

在 ViewController 中可观察到的单元测试 RxSwift

ngFor正在根据可观察变量的变化重新渲染内容

RxSwift - 可观察的自定义类

Rxswift 在事件发生时取消观察者并重新订阅