7

低レベルデバイスの状態(の変化)をポーリングすることによって生成された、観測可能なソースが与えられます。

// 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を回避するには、Throttleが別のスレッドで動作するため、 2つのObserveOn(uiSynchronizationContext)が必要です。

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

2 に答える 2

7

タイムアウトは、多かれ少なかれ、単一の非同期操作を表すオブザーバブルを意味します。たとえば、デフォルト値を返す場合やOnError、オブザーバブルが特定の時間内に通知しなかった場合などです。

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

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

于 2012-10-08T18:16:07.870 に答える
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());
于 2012-10-09T10:31:28.123 に答える