3

In my opinion, I have a pretty good "feel" for RX functions - I use many of them or can imagine how other can be useful - but I can't find a place for the .Prune function. I know that this is a Multicast to AsyncSubject, but how this can be useful in a real scenario?

Edit: Richard says WebRequest is a good candidate for Prune(). I still don't see how. Let's take an example - I want to transform incoming uri's to images:

    public static IObservable<BitmapImage> ToImage(this IObservable<string> source)
    {
        var streams = 
            from wc in source.Select(WebRequest.Create)
            from s in Observable
                .FromAsyncPattern<WebResponse>(wc.BeginGetResponse, 
                    wc.EndGetResponse)()
                .Catch(Observable.Empty<WebResponse>())
            select s.GetResponseStream();
        return streams
            .ObserveOnDispatcher()
            .Select(x =>
                        {
                            var bmp = new BitmapImage();
                            bmp.SetSource(x);
                            return bmp;
                        });
    }

I don't see it necessary to append .Prune to .FromAsyncPattern, because when you're calling FromAsyncPattern() (which is hot) you subscribe "instantly".

Sergey Aldoukhov
  • 22,316
  • 18
  • 72
  • 99

3 Answers3

3

As it was confirmed on the RX Forum Prune is just a covenience operator.

If your observable has a single value and you're publishing it, can replace Publish\Connect with a single call to .Prune()

So from my experience, the most common scenario for Prune is:

  • You have a cold observable that produces side-effects and emits only one value
  • You have more than one subscriber to that observable, so you want to make it hot (because of side-effects)

Another, pointed out in the forum, is when you need to cache a particular value on a hot observable(usually first). Then you use FromEvent(...).Take(1).Prune() and anybody who subscribes to this will get the same value guaranteed. This one is not just "convenience", but pretty much the only easy way to achieve the result.

Pretty useful, after all!

Sergey Aldoukhov
  • 22,316
  • 18
  • 72
  • 99
2

The most common scenario is when the source observable is hot and can complete before you subscribe to it. The AsyncSubject captures the last value and re-emits it for any future subscribers.

Edit

I'd have to check, but I believe FromAsyncPattern uses an AsyncSubject internally, so is actually already "Pruned".

However, assuming you were working with some other hot source that did not, the use of Prune comes entirely down to the lifetime of the IObservable before it is subscribed to. If you subscribe to it instantly, there is no need for Prune. If the IObservable will exist for a while before being subscribed to, however, it may have already completed.

This is my understanding, as someone who has ported Rx but never used Prune. Maybe you should ask the same question on the Rx forums? You've got a chance of it being answered by someone on the Rx team.

Richard Szalay
  • 83,269
  • 19
  • 178
  • 237
  • Somehow in my scenarios hot observables are running until the container is being destroyed. So I can re-phrase my question to "give me an example of a task (web request, UI interaction, etc.) when I need last value of a hot observable." – Sergey Aldoukhov Feb 12 '11 at 18:48
  • You said it yourself, web requests are a perfect situation for Prune. – Richard Szalay Feb 12 '11 at 18:57
  • See my edit. Can you explain WHY on a concrete code example? Like "if you don't put Prune in here this bad thing might happen". – Sergey Aldoukhov Feb 12 '11 at 21:06
1

I've also found a neat use for it when I've got multiple UI components that need to listen to a task (e.g. callback) and by default, Subscribe() on a cold observable will kick off that task several times which is typically not what you want when sharing state across UI components.

I know Richard mentioned a lot of these points but I figured this is such a perfect candidate for single-run Tasks, to add this example in too.

var oTask = Observable.FromAsync(() => Task.Factory.StartNew(() =>
                                                             {
                              Thread.Sleep(1000);
                              Console.WriteLine("Executed Task");
                                                             }));

//Setup the IConnectedObservable
var oTask2 = oTask.PublishLast();

//Subscribe - nothing happens
oTask2.Subscribe(x => { Console.WriteLine("Called from Task 1"); });
oTask2.Subscribe(x => { Console.WriteLine("Called from Task 2"); });

//The one and only time the Task is run
oTask2.Connect();

//Subscribe after the task is already complete - we want the results
Thread.Sleep(5000);
oTask2.Subscribe(x => { Console.WriteLine("Called from Task 3"); });
nchaud
  • 377
  • 3
  • 13