Flack
Flack

Reputation: 5899

Using RX to synchronize multiple events

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

Answers (1)

JerKimball
JerKimball

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

Related Questions