我正在编写一个函数IsAlive
来获取IObservable<T>
和一个时间跨度,并返回一个IObservable<bool>
规范用例是检测流服务器是否仍在发送数据
我已经为它提出了以下解决方案,但感觉它的工作原理并不是最明确的。
public static IObservable<bool> IsAlive<T>(this IObservable<T> source,
TimeSpan timeout,
IScheduler sched)
{
return source.Window(timeout, sched)
.Select(wind => wind.Any())
.SelectMany(a => a)
.DistinctUntilChanged();
}
有没有人有更好的方法?
仅供参考 - 以下是我尝试过的单元测试和现有方法:https://gist.github.com/997003
答案 0 :(得分:1)
怎么样:
source.Select(_ => true)
.Timeout(timeout, sched)
.DistinctUntilChanged()
.Catch<bool, TimeoutException>)(ex => Observable.Return(false));
答案 1 :(得分:1)
这应该有效:
public static IObservable<bool> IsAlive<T>(this IObservable<T> source,
TimeSpan timeout,
IScheduler sched)
{
return source.Buffer(timeout, 1, sched)
.Select(l => l.Any())
.DistinctUntilChanged();
}
这种方法也具有语义意义。每次项目进入时,它都会填充缓冲区,然后传递true。每次超时,都会创建一个空缓冲区,并传递false。
修改强>
这就是缓冲区1方法优于窗口化的原因:
var sched = new TestScheduler();
var subj = new Subject<Unit>();
var timeout = TimeSpan.FromTicks(10);
subj
.Buffer(timeout, 1, sched)
.Select(Enumerable.Any)
.Subscribe(x => Console.WriteLine("Buffer(timeout, 1): " + x));
subj
.Window(timeout, sched)
.Select(wind => wind.Any())
.SelectMany(a => a)
.Subscribe(x => Console.WriteLine("Window(timeout): "+x));
sched.AdvanceTo(5);
subj.OnNext(Unit.Default);
sched.AdvanceTo(16);
的产率:
Buffer(timeout, 1): True
Window(timeout): True
Buffer(timeout, 1): False
具体来说,窗口在整个超时时间内打开,并且一旦项目进入就不会关闭和重置。这是缓冲区限制为1的地方。一旦项目进入,缓冲区及其计时器就会重新启动。
我可以将缓冲区重新实现为一个窗口,因为缓冲区的实现是一个窗口,但是a)我认为缓冲区具有更好的语义感,而b)我没有SelectMany。 Scott的Select和SelectMany可以组合成一个SelectMany(x =&gt; x.Any()),但是我可以避免整个lambda并指定Enumerable.Any方法组,无论如何它将更快地绑定(平凡)。