我应该如何检测一个observable是否空闲并每分钟注入数据?

时间:2011-12-21 05:17:56

标签: c# windows-phone-7 system.reactive

我偶尔会有一个可观察的返回数据。如果一分钟没有数据,我需要每分钟重复最后一次数据,直到它再次生成数据。我怎样才能做到这一点?

谢谢。

5 个答案:

答案 0 :(得分:2)

这是一个可以做你想做的内衬。我已经测试了它,似乎是正确的。

var results = source
    .Publish(xs =>
        xs
            .Select(x =>
                Observable
                    .Interval(TimeSpan.FromMinutes(1.0))
                    .Select(_ => x)
                    .StartWith(x))
            .Switch());

如果这样做,请告诉我。

答案 1 :(得分:1)

我会将observable包装在一个observable中,保证每分钟至少返回一次值。包装器可以通过运行一个定时器来完成此操作,只要包装的observable返回一个值,就会重新启动。

因此,只要包装的observable返回数据或者在最后一个事件之后经过一分钟,包装器就会返回数据。

应用程序的其余部分可以方便地观察包装器。

答案 2 :(得分:0)

这是一个(非线程安全的,如果您的源是多线程的)RepeatAfterTimeout运算符的实现:

编辑更新为超时无法按预期工作

// Repeats the last value emitted after a timeout 
public static IObservable<TSource> RepeatAfterTimeout<TSource>(
    this IObservable<TSource> source, TimeSpan timeout, IScheduler scheduler)
{
    return Observable.CreateWithDisposable<TSource>(observer =>
    {
        var timer = new MutableDisposable();
        var subscription = new MutableDisposable();

        bool hasValue = false;
        TSource lastValue = default(TSource);

        timer.Disposable = scheduler.Schedule(recurse =>
        {
            if (hasValue)
            {
                observer.OnNext(lastValue);
            }

            recurse();
        });

        subscription.Disposable = source
            .Do(value => { lastValue = value; hasValue = true; })
            .Subscribe(observer);

        return new CompositeDisposable(timer, subscription);
    });
}

public static IObservable<TSource> RepeatAfterTimeout<TSource>(
    this IObservable<TSource> source, TimeSpan timeout)
{
    return source.RepeatAfterTimeout(timeout, Scheduler.TaskPool);
}

答案 3 :(得分:0)

我有一次完全相同的要求。如果在第一次超时之前没有触发值,我选择包含一个默认值。这是C#版本:

public static IObservable<T> 
AtLeastEvery<T>(this IObservable<T> source, TimeSpan timeout, 
                T defaultValue, IScheduler scheduler)
{
    if (source == null) throw new ArgumentNullException("source");
    if (scheduler == null) throw new ArgumentNullException("scheduler");
    return Observable.Create<T>(obs =>
        {
            ulong id = 0;
            var gate = new Object();
            var timer = new SerialDisposable();
            T lastValue = defaultValue;

            Action createTimer = () =>
                {
                    ulong startId = id;
                    timer.Disposable = scheduler.Schedule(timeout,
                          self =>
                          {
                              bool noChange;
                              lock (gate)
                              {
                                  noChange = (id == startId);
                                  if (noChange) obs.OnNext(lastValue);
                              }
                              //only restart if no change, otherwise
                              //the change restarted the timeout
                              if (noChange) self(timeout);
                          });
                };
            //start the first timeout
            createTimer();
            var subscription = source.Subscribe(
                v =>
                {
                    lock (gate)
                    {
                        id += 1;
                        lastValue = v;
                    }
                    obs.OnNext(v);
                    createTimer(); //reset the timeout
                },
                ex =>
                {
                    lock (gate)
                    {
                        id += 1; //'cancel' timeout
                    }
                    obs.OnError(ex);
                    //do not reset the timeout, because the sequence has ended
                },
                () =>
                {
                    lock (gate)
                    {
                        id += 1; //'cancel' timeout
                    }
                    obs.OnCompleted();
                    //do not reset the timeout, because the sequence has ended
                });

            return new CompositeDisposable(timer, subscription);
        });
}

如果您不想每次都必须传递一个调度程序,只需进行一个选择默认调度的重载并委托给该方法。我使用了Scheduler.ThreadPool

当代码中的新值进入时,此代码使用SerialDisposable的行为来“取消”先前的超时调用。还有一个计数器,用于计时器已经过去(在哪种情况处理从Schedule中返回将无济于事)但该方法尚未实际运行。

我调查了更改超时的可能性,但是我没有把它用于我正在处理的问题。我记得这是可能的,但没有任何代码方便。

答案 4 :(得分:0)

我认为这应该是Rx方式(没有递归,但仍然涉及副作用):

    public static IObservable<TSource> RepeatLastValueWhenIdle<TSource>(
        this IObservable<TSource> source,
        TimeSpan idleTime,
        TSource defaultValue = default(TSource))
    {
        TSource lastValue = defaultValue;

        return source
            // memorize the last value on each new 
            .Do(ev => lastValue = ev) 
            // re-publish the last value on timeout
            .Timeout(idleTime,  Observable.Return(lastValue))
            // restart waiting for a new value
            .Repeat(); 
    }