我需要在两个状态之间交替,每个状态具有不同的间隔时间 我能想到这样做的最好方法是使用Reactive Extensions的Observable.Generate 这非常棒。
从我在msdn和其他网站上看到的内容,Observable.Finally()应该触发 可观察的“优雅地或特殊地终止”。我正在测试以下代码 (在LINQPad中)看看它是如何工作的,但我根本无法得到.Finall()。
var ia = TimeSpan.FromSeconds(1);
var ib = TimeSpan.FromSeconds(.2);
var start = DateTime.UtcNow;
var ct = new CancellationTokenSource();
var o = Observable.Generate(
true,
// s => !ct.IsCancellationRequested,
s => (DateTime.UtcNow-start) < TimeSpan.FromSeconds(3) && !ct.IsCancellationRequested,
s => !s,
s => s ? "on" : "off",
s => s? ib : ia)
// .TakeUntil(start+TimeSpan.FromSeconds(3))
.Concat(Observable.Return("end"));
o.Subscribe( s=> s.Dump(), ct.Token);
var t = o.ToTask(ct.Token);
t.ContinueWith(x => x.Dump("done"));
o.Finally(() => "finallY".Dump()); // never gets called?
Thread.Sleep(10000);
ct.Cancel();
如果我使Thread.Sleep 10s,可观察序列完成并且Task.ContinueWith触发, 但不是.Finally()。
如果我创建Thread.Sleep 2s,则会取消可观察序列并再次触发Task.ContinueWith, 但不是.Finally()。
为什么不呢?
答案 0 :(得分:5)
查看Finally
方法的返回类型;应该给你一个提示。就像Concat
方法返回 new IObservable
并将新序列连接到它,但不会更改原始序列时,Finally
方法返回一个新的已完成最后操作的IObservable
,但您订阅了原始IObservable
。将以下行放在Subscribe
来电之前,它就能正常工作。
o = o.Finally(() => "finallY".Dump());
我同意这是一个奇怪的API选择;我认为Finally
更像是Subscribe
而不是Concat
。你订阅到最后的“事件”;奇怪的是,API 强制你创建一个全新的 IObservable ,然后订阅它只是为了让Finally
事情发生。另外,它允许潜在的错误(如果我们在你的问题中使用函数,这很明显),如果你订阅两次新的 IObservable ,你的Finally
函数将执行两次。因此,您必须确保您的某个订阅位于“finallied” IObservable 上,而其他订阅都在原始版本上。看起来很不寻常。
我想考虑它的方法是Finally
不是要修改observable,而是修改订阅本身。也就是说,他们通常不希望您制作具有Finally
内容(var o = Observable.[...].Finally(...);
)的可公开访问的命名可观察对象,而是要与订阅调用本身内联(var subscription = o.Finally(...).Subscribe(...);
)< / p>