2

I'm trying to create a single Observable which OnNext stream comes from one observable and which OnError stream comes from another observable.

The reason why I'm doing this is because I'm trying to wrap a class that is outside of my control and uses events to communicate its state. It has two events, one that indicates its done (bool) and one that indicates that an Exception occurs.

IObservable<Exception> error = Observable.FromEventPattern<ExceptionRoutedEventArgs>(foo, "Failed")
               .Select(x => x.EventArgs.ErrorException);

IObservable<bool> opened = Observable.FromEventPattern<RoutedEventArgs>(foo, "Opened")
               .Select(x => ((Bar)x.Sender).IsOpen);

Now I cannot use the standard Observable.Merge since both observable have a different generic parameter. But In pseudo code I would like to accomplish this:

Observable.Merge(opened, error, (op, err) =>
{
    if(op) { return op;}
    if(err != null){return Observable.Throw(err);}
}

Now there are a lot of reasons why the code above doesn't remotely resemble anything that can exist but I hope the intent is clear.

I think a way to make this work is to use a Subject<> but I've heard that those should be avoid that since it introduces state in a functional concept. And I have the idea that combining two observables into one observables OnNext and OnError stream seems like it should exist :)

Roy T.
  • 9,429
  • 2
  • 48
  • 70

2 Answers2

4

You could use Observable.Create:

var combined = Observable.Create<bool>(o =>
{
    var openSub = opened.Subscribe(o);
    var errorSub = error.Subscribe(o.OnError);
    return new CompositeDisposable(openSub, errorSub);
});

Or you could project each Exception to an IObservable<bool> that just throws the errors, and merge the resulting sequences:

var combined = opened.Merge(error.SelectMany(Observable.Throw<bool>));

Though just because your source uses Exceptions for control flow doesn't mean you have to - why not just project the Exception to false, then you just get true on success and false on failure:

var combined = opened.Merge(error.Select(_ => false));
Charles Mager
  • 25,735
  • 2
  • 35
  • 45
  • Thanks, both solutions look great! I'm including the Exceptions because I want to log them later one. Hence why I can't use your third suggestion. – Roy T. Jun 09 '16 at 09:40
  • Remember that if your sequence errors, then it also terminates. i.e. if you get a value (exception) on your error sequence, and yield that as an `OnError` in your output sequence, it will be the last thing in that sequence. So if you want to keep listening to further values, this may not be what you are looking for, or perhaps you can slap a `.Repeat()` on it. – Lee Campbell Jun 10 '16 at 08:03
1

Try this:

var combined =
    opened
        .Materialize()
        .Merge(error.Select(e => Notification.CreateOnError<bool>(e)))
        .Dematerialize()
        .Synchronize();
Enigmativity
  • 113,464
  • 11
  • 89
  • 172