Reputation: 5899
Let's say I have three products in a list. In order to enable a certain action, all three need to be of a certain type. In order to find out the type of the product, I need to make a service call and wait for a response.
What I would like to do is wait for all three responses (maybe with a timeout in case something goes wrong) and when all the info is gathered, decide whether or not to enable the possible action.
I used to solve this by having some counter or reset events to keep track of the finished events but I would like to see if I can use Rx to do it in a cleaner way.
As I am not too familiar with Rx yet, I am looking for some tips/pointers. I understand I can use
Observable.FromEventPattern
for the events I am waiting on. I subscribe and wait for the response and handle it. I am just not clear on how to combine the multiple events.
Upvotes: 2
Views: 1887
Reputation: 16894
The combinator you are looking for is CombineLatest
Say you've got a class like this:
public class Foo
{
public delegate void FooEventHandler(object sender, EventArgs args);
public event FooEventHandler FirstEvent = delegate {};
public event FooEventHandler SecondEvent = delegate {};
public event FooEventHandler ThirdEvent = delegate {};
public void DoIt()
{
FireOne();
FireTwo();
FireThree();
}
public void FireOne()
{
Console.WriteLine("Firing event 1...");
Thread.Sleep(1000);
FirstEvent(this, new EventArgs());
}
public void FireTwo()
{
Console.WriteLine("Firing event 2...");
Thread.Sleep(1000);
SecondEvent(this, new EventArgs());
}
public void FireThree()
{
Console.WriteLine("Firing event 3...");
Thread.Sleep(1000);
ThirdEvent(this, new EventArgs());
}
}
First you'd want to "convert" those events to Observable
:
var foo = new Foo();
var firstWatcher = Observable.FromEventPattern(foo, "FirstEvent");
var secondWatcher = Observable.FromEventPattern(foo, "SecondEvent");
var thirdWatcher = Observable.FromEventPattern(foo, "ThirdEvent");
Now you'll want the "Only fire when all these have fired" selector, which is CombineLatest
:
var allDone = Observable.CombineLatest(firstWatcher, secondWatcher, thirdWatcher);
And to test it out:
using(allDone.Subscribe(_ => Console.WriteLine("Boop! You sunk my battleship!")))
{
foo.DoIt();
}
Alternative "test harness":
var foo = new Foo();
var firstWatcher = Observable.FromEventPattern(foo, "FirstEvent");
var secondWatcher = Observable.FromEventPattern(foo, "SecondEvent");
var thirdWatcher = Observable.FromEventPattern(foo, "ThirdEvent");
var allDone = Observable.CombineLatest(firstWatcher, secondWatcher, thirdWatcher);
// keep a handle on the subscription
IDisposable subscription = null;
// to prevent premature exiting...
var blocker = new ManualResetEvent(false);
// explicit subscribe
subscription = allDone.Subscribe(
whoCares =>
{
Console.WriteLine("BOOM! We're done!");
// always clean up after yourself
if(subscription != null)
{
subscription.Dispose();
}
// it's ok, we can quit now
blocker.Set();
});
foo.DoIt();
// Wait until it's clear to go ahead...
blocker.WaitOne();
Upvotes: 4