Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Observable.Finally not called when Observable.Generate finishes?

I needed to alternate between two states with each state having a different interval time.
The best way I could think of doing this was to use Reactive Extensions' Observable.Generate which is pretty awsome.

From what I read on msdn and other sites, Observable.Finally() should fire if the observable "terminates gracefully or exceptionally". I was testing the following code (in LINQPad) to see how it works, but I can not get .Finall() to fire at all.

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();

If I make Thread.Sleep 10s, the observable sequence finishes and the Task.ContinueWith fires, but not .Finally().

If I make Thread.Sleep 2s, the observable sequence is canceled and the Task.ContinueWith again fires, but not .Finally().

Why not?

like image 582
bj0 Avatar asked Sep 03 '25 02:09

bj0


1 Answers

Look at the return type of the Finally method; should give you a hint. Just like the Concat method returns a new IObservable with the new sequence concatenated to it, but doesn't change the original, the Finally method returns a new IObservable that has that final action, but you're subscribing to the original IObservable. Put the following line in front of your Subscribe call and it'll work.

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

I agree it's an odd API choice though; I'd think of Finally as being more akin to Subscribe than to Concat. You're subscribing to the finally "event"; it's odd that the API forces you to create a completely new IObservable and then subscribe to that just to get the Finally thing to happen. Plus it allows a potential error (made evident if we use the function in your question) that if you subscribe twice to that new IObservable, your Finally function will execute twice. So you have to make sure that one of your subscriptions is on the "finallied" IObservable and the others are all on the original. Just seems unusual.

I guess the way to think about it is that Finally isn't meant to modify the observable, but rather to modify the subscription itself. i.e., they don't expect you typically to make openly-accessible named observables that have Finally things (var o = Observable.[...].Finally(...);) rather it's meant to go inline with the subscription call itself (var subscription = o.Finally(...).Subscribe(...);)

like image 163
Dax Fohl Avatar answered Sep 04 '25 16:09

Dax Fohl