سؤال

I have a method that returns an observable. This observable should (if evetything is working right) publish a value each second. What I would like to do is have it publish some custom alert value if a certain time has passed with no output.

private IObservable<string> GetStatus()
{
    return statusProvider
                .Subscribe(statusKey)  //returns an IObservable<string>
                .Select(st => st.ToUpper())
                .DistinctUntilChanged()
                .TakeUntil(disposed)
                .Replay(1)
                .RefCount();
}

Is there a simple way for me to modify the above so that if no status update has come in for 30 seconds, the statusProvider publishes "bad" and then if an update does come in after that, it get published as usual and the timer is restarted to 30 secs again?

هل كانت مفيدة؟

المحلول

Here is a way. Starts a timer which will yield "bad" when it expires. Each time your statusProvider produces a status, the timer gets reset.

var statusSignal = statusProvider
            .Subscribe(statusKey)  //returns an IObservable<string>
            .Select(st => st.ToUpper())
            .Publish()
            .RefCount();

// An observable that produces "bad" after a delay and then "hangs indefinately" after that
var badTimer = Observable
    .Return("bad")
    .Delay(TimeSpan.FromSeconds(30))
    .Concat(Observable.Never<string>());

// A repeating badTimer that resets the timer whenever a good signal arrives.
// The "indefinite hang" in badTimer prevents this from restarting the timer as soon
// as it produces a "bad".  Which prevents you from getting a string of "bad" messages
// if the statusProvider is silent for many minutes.
var badSignal = badTimer.TakeUntil(statusSignal).Repeat();

// listen to both good and bad signals.
return Observable
    .Merge(statusSignal, badSignal)
    .DistinctUntilChanged()
    .Replay(1)
    .RefCount();

نصائح أخرى

I think that the following should work. It uses Throttle which will wait until 30 seconds have passed with no input before sending anything. You can then merge this with your preexisting source to get your desired behaviour.

var bad = source
    .Throttle(TimeSpan.FromSeconds(30))
    .Select(_ => "bad");

var merged = source.Merge(bad);

This is very easy to do:

    source
        .Select(x =>
            Observable
                .Interval(TimeSpan.FromSeconds(30.0))
                .Select(y => "bad")
                .StartWith(x))
        .Switch();

I've simplified your query down to source. On every value coming in you start an interval observable that produces "bad" every 30 seconds, but you start the observable with the incoming value. Then you just .Switch() on this observable.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top