簡體   English   中英

Rx框架:在超時時執行操作,而不會中斷原始的可觀察序列

[英]Rx Framework: execute an action on timeout without interrupting the original observable sequence

給定一個可觀察的源,通過輪詢低級設備的狀態(a的變化)生成...

// observable source metacode:
IObservable<DeviceState> source = Observable.Interval(TimeSpan.FromSeconds(0.5))
    .Select(tick => new DeviceState(_device.ReadValue()))
    .DistinctUntilChanged();

......以及更新UI的消費者......

// UI metacode:
service.GetObservableDeviceStates()
    .Subscribe(state => viewModel.CurrentState = state.ToString());

...我需要在x秒的源“不活動”后執行自定義操作,而不會中斷對源的訂閱。 像這樣的東西:

// UI metacode:
service.GetObservableDeviceStates()
    .DoOnTimeout(TimeSpan.FromSeconds(x), () => viewModel.CurrentState = "Idle")
    .Subscribe(state => viewModel.CurrentState = state.ToString());

什么是最佳做法? 想到的可能解決方案是(我是Rx noob):

  1. 緩沖區 (即使它不那么可讀)
  2. 這個超時超載 ;
  3. 當沒有任何變化(而不是使用DistinctUntilChanged)並在UI代碼上處理它時,返回一些特殊的“服務端”:

    service.GetObservableDeviceStates()。Subscribe(state => viewModel.CurrentState = state.Special?“Idle”:state.ToString());

編輯:如答案中所述 ,解決方案是:

        service.GetObservableDeviceStates()
            .Do(onNext)
            .Throttle(TimeSpan.FromSeconds(x))
            .Subscribe(onTimeout);

EDIT2(警告)

如果onNext和onTimeout更新UI組件,為了避免CrossThreadExceptions需要兩個 ObserveOn(uiSynchronizationContext),因為Throttle在另一個線程上工作!

        service.GetObservableDeviceStates()
            .ObserveOn(uiSynchronizationContext)
            .Do(onNext)
            .Throttle(TimeSpan.FromSeconds(x))
            .ObserveOn(uiSynchronizationContext)
            .Subscribe(onTimeout);

超時或多或少意味着代表單個異步操作的可觀察對象 - 例如,如果所述observable在一定時間內沒有通知您,則返回默認值或OnError

你正在尋找的運營商是Throttle ,即使它起初可能看起來不像。 Throttle(p)為您提供一個流,當源流未生成句點p的值時,該流生成一個值。

與現有代碼並行,您可以使用source.Throttle(period).Do(...side effect)

我個人會為此避免使用Do方法。 它確實使這個例子中的代碼相當容易,但我發現一旦使用'Do'潛入代碼庫,你很快就會有意大利面。

您還可以考慮使用Amb,Timer,TakeUntil,Throttle等組合來獲得您正在尋找的結果並仍然保持Monad *。 或者簡單來說,我假設您理想情況下希望有一系列狀態值通過而不需要在您的代碼中放置一個計時器(即將其卸載到服務中)。

public IObservable<DeviceStatus> GetObservableDeviceStates(TimeSpan silencePeriod)
{
    return Observable.Create<DeviceStatus>(
    o=>
    {
        var idle = Observable.Timer(silencePeriod).Select(_=>new DeviceStatus("Idle"));

        var polledStatus = Observable.Interval(TimeSpan.FromSeconds(0.5))
                        .Select(tick => new DeviceStatus(_device.ReadValue()))
                        .DistinctUntilChanged()
                        .Publish();

        var subscription = (from status in polledStatus
                            from cont in Observable.Return(status).Concat(idle.TakeUntil(polledStatus))
                            select cont)
                     .Subscribe(o);

        return new CompositeDisposable(subscription, polledStatus.Connect());
    });
}

此代碼現在使服務在指定的更改靜默時間段后返回空閑狀態值。

這意味着您的UI元代碼保持簡單,與DeviceStatus相關的邏輯保持在它所屬的位置

// UI metacode:
service.GetObservableDeviceStates(TimeSpan.FromSeconds(2))
    .Subscribe(state => viewModel.CurrentState = state.ToString());

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM