2012-10-08 3 views
7

を中断することなくRxのフレームワークは:...ポーリング低レベルのデバイスの状態(の変化)によって生成された観察光源与えられた原観察シーケンス

// 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. Buffer(それはそう読めるない場合でも)
  2. this Timeout overload遊ん。
  3. 何が(代わりにDistinctUntilChangedを使用しての)変化しない何か特別な "サービス側" を返すと、UIのコードでそれを扱う:

    service.GetObservableDeviceStates() .Subscribe(状態=> viewModel.CurrentState = state.Special? "Idle":state.ToString());

EDIT:in the answerが報告されているように、溶液である:

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

EDIT2(警告)

onNextとOnTimeoutパス更新UIコンポーネント場合、CrossThreadExceptionsを回避する ObserveOn(uiSynchronizationContext) Throttleは別のスレッドで動作するため、必要です。一定の時間であなたを通知していない観察できると述べている場合、例えばのためにデフォルト値を返すことかOnError -

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

に属しどことどまるあなたに、そこにスロットリングタイマーを実行するために絞るパラメータとしてUIスレッドスケジューラを指定できることを意味します追加のObserveOnホップを避けたい場合。 –

+0

@BartDeSmet私はすでにThrottle(x、Scheduler.CurrentThread)を試しましたが、私はUIの応答性を失っていました...私の場合、ObserveOnはうまく機能しました – Notoriousxl

答えて

7

タイムアウトは、多かれ少なかれ、単一の非同期操作を表す観測するためのものです。

あなたが探しているオペレータは、最初はそれほど見えないかもしれませんが、Throttleです。 Throttle(p)は、ソースストリームが期間pの値を生成していないときに値を生成するストリームを提供します。

既存のコードと並行して、source.Throttle(period).Do(...side effect)を使用できます。

+0

魅力のように動作します。私は常にスロットルを使用して要素を破棄し(最後のサブスクリプションで残りの要素を消費する)、決してonPauseコールバックを呼び出しませんでした:) – Notoriousxl

5

私は個人的にこれを行うための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()); 
+1

+1は「Do」が特定の種類悪の私は、 'Timestamp' |' CombineLatest'がこのケースでもう少しパフォーマンスが良いかもしれないと感じます。 – Asti

+0

感じる?私たちは応用科学で働いています。ちょうど証拠を提供してください。感情に基づいてパフォーマンスの推奨事項を宣言することは、私たちの業界では止める必要がある問題です。 –

+0

聖なるネクロマンシー、バットマン!ポイントはリーを取った。私はそれのためにより良い人になるでしょう。 :) – Asti

関連する問題