2013-07-10 38 views
2

我需要在兩個狀態之間交替,每個狀態具有不同的間隔時間。
我能想到這樣做的最好方法是使用Reactive Extensions的Observable.Generate ,這非常可怕。Observable.Generate完成後,爲什麼Observable.Finally不會被調用?

從我在msdn和其他網站上閱讀的內容可以看出,如果 observable「優雅或異常終止」,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火災, 但不是。最後()。

如果我創建Thread.Sleep 2s,可觀察序列被取消,Task.ContinueWith再次觸發,但不是.Finally()。

爲什麼不呢?

回答

5

看看Finally方法的返回類型;應該給你一個提示。就像Concat方法返回一個新的IObservable與新的序列連接到它,但不會更改原始,Finally方法返回一個新的IObservable具有該最終操作,但您正在訂閱原始IObservable。將以下行放在Subscribe呼叫的前面,它會起作用。

o = o.Finally(() => "finallY".Dump()); 

雖然我同意這是一個奇怪的API選擇;我認爲Finally更接近Subscribe而不是Concat。你是訂閱到最後的「事件」;奇怪的是,API 強制您創建一個全新的IObservable,然後訂閱以獲得Finally事情發生。此外,它允許一個潛在的錯誤(如果我們在你的問題中使用該功能就顯而易見),如果你訂購了兩次新的功能,你的Finally函數將執行兩次。所以你必須確保你的一個訂閱是在「最後的」IObservable,其他的都在原件上。看起來很不尋常。

我猜想的方式是Finally並不是要修改observable,而是修改訂閱本身。也就是說,他們並不期望你通常可以公開訪問具有Finally東西(var o = Observable.[...].Finally(...);)的可觀察名稱的觀察值,而是意味着與預訂調用本身內聯(var subscription = o.Finally(...).Subscribe(...);

+0

啊,這是有道理的。最後一種避免多次調用的方法是,不要替換爲原始的IObservable,而只是使用新的單一Subscribe。只需將一行改爲:o。最後(()=>「finallY」.Dump())。Subscribe();' – bj0

相關問題