我偶尔会有一个可观察的返回数据。如果一分钟没有数据,我需要每分钟重复最后一次数据,直到它再次生成数据。我怎样才能做到这一点?
谢谢。
答案 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();
}