I need to have a hot observable which wraps a price feed. This is subscribed in multiple areas.
The observable is created using Refcount and passed around for subscription.The first subscriber calls subscribe hence starts the stream and get all the events. The second will miss the events until it's subscription and subsequent subscriptions will do the same behaviour.
It is not the missing of events which is my issue. I want all subscribers to get the same data. That is, the stream must start only when the subscription requests are finished.
Is it possible?
Edit: Two approaches and its problems are illustrated below.
1)
public void HotObservableSubscriptionWithRefCount()
{
var obs1 = Observable.Interval(TimeSpan.FromMilliseconds(500)).Select(x =>
{
var publishVal = x;
Console.WriteLine($#"observer1 publishing {publishVal}");
return publishVal;
}).Publish().RefCount();
var obs2 = Observable.Interval(TimeSpan.FromMilliseconds(500)).Select(x =>
{
var publishVal = x + 100;
Console.WriteLine($#"observer2 publishing {publishVal}");
return publishVal;
}).Publish().RefCount();
var sub1 = obs1.Subscribe(x => Console.WriteLine($#"subscriber1 value {x}"));
var sub2 = obs2.Subscribe(x => Console.WriteLine($#"subscriber2
value {x}"));
Thread.Sleep(TimeSpan.FromSeconds(1));
var combinedSub = obs1.Merge(obs2).Subscribe(x => Console.WriteLine($#"combined
subscriber value {x}"));
Thread.Sleep(TimeSpan.FromSeconds(1));
sub1.Dispose();
sub2.Dispose();
combinedSub.Dispose();
Thread.Sleep(TimeSpan.FromSeconds(1));
}
Problem: The combined subscriber is missing values from two observables because of the delay in subscription
2)
public void HotObservableSubscriptionWithPublish()
{
var obs1 = Observable.Interval(TimeSpan.FromMilliseconds(500)).Select(x =>
{
var publishVal = x;
Console.WriteLine($#"observer1 publishing {publishVal}");
return publishVal;
}).Publish();
var obs2 = Observable.Interval(TimeSpan.FromMilliseconds(500)).Select(x =>
{
var publishVal = x + 100;
Console.WriteLine($#"observer2 publishing {publishVal}");
return publishVal;
}).Publish();
var sub1 = obs1.Subscribe(x => Console.WriteLine($#"subscriber1 value
{x}"));
var sub2 = obs2.Subscribe(x => Console.WriteLine($#"subscriber2 value
{x}"));
Thread.Sleep(TimeSpan.FromSeconds(1));
var combinedSub = obs1.Merge(obs2).Subscribe(x =>
Console.WriteLine($#"combined subscriber value {x}"));
obs1.Connect();
obs2.Connect();
Thread.Sleep(TimeSpan.FromSeconds(1));
sub1.Dispose();
sub2.Dispose();
combinedSub.Dispose();
Thread.Sleep(TimeSpan.FromSeconds(1));
}
This will make sure combinedsubsciber will get values in line with any individual subscribers. However even the subscribers are disposed of, the observer still continue provide values.
I need full life cycle control of publisher and subscriber
As pointed out by Felix Keil in a comment, the solution is to use the second approach, and disconnect the connectable observables by disposing the two connections.
var conn1 = obs1.Connect();
var conn2 = obs2.Connect();
/* ... */
conn2.Dispose();
conn1.Dispose();
Related
I am writing some code using RX in C# that must interface with an older system by emitting events.
In summary, I have an observable and need to emit one event when the observable completes and another event if a timeout exception is detected. The main problem is how best to handle the exception.
I'm relatively new to RX, so although I have found a solution, I can't be sure that there isn't a better or more appropriate way that uses the RX extensions better.
This is not the real code but indicates the pattern of my thinking:
public delegate void SuccessHandler(object sender, SuccessEventArgs e);
public event SuccessHandler OnSuccess;
public delegate void TimeoutHandler(object sender, TimeoutEventArgs e);
public event TimeoutHandler OnTimeout;
var id;
var o = Observable.Return() // <- this would be a fetch from an asynchronous source
.Where(r=>r.status=="OK")
.Timeout(new Timespan(0,0,30)
.Do(r=> {
id=r.Id // <-- Ugh! I know this shouldn't be done!
}
.Subscribe(r => {
var statusResponse= new StatusResponse()
{
Id = r.Id
Name = r.Name
Message = "The operation completed successfully",
Status = Status.Success
};
if (OnSuccess == null) return;
OnSuccess (this, new SuccessEventArgs(statusResponse);
},
e =>
{
_logger.LogError(e, "A matching response was not returned in a timely fashion");
if (OnTimeout == null) return;
OnTimeout(this, new TimeoutEventArgs(id));
});
If I didn't need to detect and act upon the timeout it would be fine; I have already worked out how to substitute the Subscribe for ToEventPattern:
...
.Select(r =>
{
var statusResponse= new StatusResponse()
{
Id = r.Id
Name = r.Name
Message = "The operation completed successfully",
Status = Status.Success
};
return new EventPattern<SuccessEventArgs>(this, new SuccessEventArgs(statusResponse));
})
.ToEventPattern();
However, I'd like to be able to detect the timeout (and possibly other exceptions). my experiments with Catch have been unsuccessful because I can't seem to get the types to line up correctly, probably because I don't really understand what is going on.
I'd very much appreciate opinions on this. Is this an acceptable solution? How can I improve it? Can anyone point me to some good online references that will explain how this kind of flow-control and exception handling can be done (all the examples I've seen so far seem to stop short of the real-world case where you want to emit an event and combine that with exception handling).
Thanks in advance
You can branch from observables quite easily, e.g.
var a = Observable.Range(0, 10);
var b = a.Select(x => x * x);
var c = a.Select(x => x * 10);
A word of warning - if the observable is cold, this will cause the producer function to run for each subscription. Look up the difference between hot and cold observables if this isn't clear.
I've created a solution that creates two branches from the source observable and turns each into an event:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var service = new Service();
var apiCall = service.CallApi();
apiCall.OnSuccess.OnNext += (_, __) => Console.WriteLine("Success!");
apiCall.OnTimeout.OnNext += (_, __) => Console.WriteLine("Timeout!");
Console.ReadLine();
}
}
class SuccessEventArgs{}
class TimeoutEventArgs{}
class ApiCall
{
public IEventPatternSource<SuccessEventArgs> OnSuccess {get;}
public IEventPatternSource<TimeoutEventArgs> OnTimeout {get;}
public ApiCall(IEventPatternSource<SuccessEventArgs> onSuccess, IEventPatternSource<TimeoutEventArgs> onTimeout)
{
OnSuccess = onSuccess;
OnTimeout = onTimeout;
}
}
class Service
{
public ApiCall CallApi()
{
var apiCall = Observable
.Timer(TimeSpan.FromSeconds(3))
.Do(_ => Console.WriteLine("Api Called"))
.Select(_ => new EventPattern<SuccessEventArgs>(null, new SuccessEventArgs()))
// .Timeout(TimeSpan.FromSeconds(2)) // uncomment to time out
.Timeout(TimeSpan.FromSeconds(4))
// the following two lines turn the "cold" observable "hot"
// comment them out and see how often "Api Called" is logged
.Publish()
.RefCount();
var success = apiCall
// ignore the TimeoutException and return an empty observable
.Catch<EventPattern<SuccessEventArgs>, TimeoutException>(_ => Observable.Empty<EventPattern<SuccessEventArgs>>())
.ToEventPattern();
var timeout = apiCall
.Materialize() // turn the exception into a call to OnNext rather than OnError
.Where(x => x.Exception is TimeoutException)
.Select(_ => new EventPattern<TimeoutEventArgs>(null, new TimeoutEventArgs()))
.ToEventPattern();
return new ApiCall(success, timeout);
}
}
Following would NOT catch the emitted value:
someObservable = Observable.FromEventPattern(this, "MyEvent");
FireMyEvent("foo");
await someObservable.FirstOrDefaultAsync(e => e.Args == "foo");
So I've come across this pattern in my unit tests:
var tsc = new TaskCompletionSource<bool>();
var callback = someObservable.FirstOrDefaultAsync(...);
using (callback.Subscribe(e => tsc.SetResult(e.Args)))
{
FireMyEvent("foo");
tsc.Task.Wait(1000);
}
Assert.AreEqual("foo", tsc.Task.Result);
Basically I need to subscribe, then trigger the action and then await the subscribed observable.
Is it possible to simplify this subscribe/await without introducing new Task, or WaitHandle?
I'm not sure what exactly you're trying to do, but if I understand the basic idea then this works:
var subject = new Subject<Unit>();
var callback = subject.FirstOrDefaultAsync();
using (callback.Subscribe(_ => { }, () => Console.WriteLine("Done.")))
{
subject.OnNext(Unit.Default);
}
Can you explain your use-case more fully?
I don't really understand your assumption, that the await won't catch the result.
You have to await the event before it gets fired
someObservable = Observable.FromEventPattern(this, "MyEvent");
Task.Delay(1000).ContinueWith(_ => FireMyEvent("foo"));
var result = await someObservable.FirstOrDefaultAsync(e => e.Args == "foo");
Your result ist now a EventPattern. However I would recommend a more type safer FromEventPattern-Overload:
var eventObservable = Observable
.FromEventPattern<YourEventArgs>(
h => MyEvent += h,
h => MyEvent -= h);
I'm trying to create an Rx operator that seems pretty useful, but I've suprisingly not found any questions on Stackoverflow that match precisely. I'd like to create a variation on Throttle that lets values through immediately if there's been a period of inactivity. My imagined use case is something like this:
I have a dropdown that kicks off a web request when the value is changed. If the user holds down the arrow key and cycles rapidly through the values, I don't want to kick off a request for each value. But if I throttle the stream then the user has to wait out the throttle duration every time they just select a value from the dropdown in the normal manner.
So whereas a normal Throttle looks like this:
I want to create ThrottleSubsequent that look like this:
Note that marbles 1, 2, and 6 are passed through without delay because they each follow a period of inactivity.
My attempt at this looks like the following:
public static IObservable<TSource> ThrottleSubsequent<TSource>(this IObservable<TSource> source, TimeSpan dueTime, IScheduler scheduler)
{
// Create a timer that resets with each new source value
var cooldownTimer = source
.Select(x => Observable.Interval(dueTime, scheduler)) // Each source value becomes a new timer
.Switch(); // Switch to the most recent timer
var cooldownWindow = source.Window(() => cooldownTimer);
// Pass along the first value of each cooldown window immediately
var firstAfterCooldown = cooldownWindow.SelectMany(o => o.Take(1));
// Throttle the rest of the values
var throttledRest = cooldownWindow
.SelectMany(o => o.Skip(1))
.Throttle(dueTime, scheduler);
return Observable.Merge(firstAfterCooldown, throttledRest);
}
This seems to work, but I'm having a difficult time reasoning about this, and I get the feeling there are some edge cases here where things might get screwy with duplicate values or something. I'd like to get some feedback from more experienced Rx-ers as to whether or not this code is correct, and/or whether there is a more idiomatic way of doing this.
Well, here's a test suite (using nuget Microsoft.Reactive.Testing):
var ts = new TestScheduler();
var source = ts.CreateHotObservable<char>(
new Recorded<Notification<char>>(200.MsTicks(), Notification.CreateOnNext('A')),
new Recorded<Notification<char>>(300.MsTicks(), Notification.CreateOnNext('B')),
new Recorded<Notification<char>>(500.MsTicks(), Notification.CreateOnNext('C')),
new Recorded<Notification<char>>(510.MsTicks(), Notification.CreateOnNext('D')),
new Recorded<Notification<char>>(550.MsTicks(), Notification.CreateOnNext('E')),
new Recorded<Notification<char>>(610.MsTicks(), Notification.CreateOnNext('F')),
new Recorded<Notification<char>>(760.MsTicks(), Notification.CreateOnNext('G'))
);
var target = source.ThrottleSubsequent(TimeSpan.FromMilliseconds(150), ts);
var expectedResults = ts.CreateHotObservable<char>(
new Recorded<Notification<char>>(200.MsTicks(), Notification.CreateOnNext('A')),
new Recorded<Notification<char>>(450.MsTicks(), Notification.CreateOnNext('B')),
new Recorded<Notification<char>>(500.MsTicks(), Notification.CreateOnNext('C')),
new Recorded<Notification<char>>(910.MsTicks(), Notification.CreateOnNext('G'))
);
var observer = ts.CreateObserver<char>();
target.Subscribe(observer);
ts.Start();
ReactiveAssert.AreElementsEqual(expectedResults.Messages, observer.Messages);
and using
public static class TestingHelpers
{
public static long MsTicks(this int i)
{
return TimeSpan.FromMilliseconds(i).Ticks;
}
}
Seems to pass. If you wanted to reduce it, you could turn it into this:
public static IObservable<TSource> ThrottleSubsequent2<TSource>(this IObservable<TSource> source, TimeSpan dueTime, IScheduler scheduler)
{
return source.Publish(_source => _source
.Window(() => _source
.Select(x => Observable.Interval(dueTime, scheduler))
.Switch()
))
.Publish(cooldownWindow =>
Observable.Merge(
cooldownWindow
.SelectMany(o => o.Take(1)),
cooldownWindow
.SelectMany(o => o.Skip(1))
.Throttle(dueTime, scheduler)
)
);
}
EDIT:
Publish forces sharing of a subscription. If you have a bad (or expensive) source observable with subscription side-effects, Publish makes sure you only subscribe once. Here's an example where Publish helps:
void Main()
{
var source = UglyRange(10);
var target = source
.SelectMany(i => Observable.Return(i).Delay(TimeSpan.FromMilliseconds(10 * i)))
.ThrottleSubsequent2(TimeSpan.FromMilliseconds(70), Scheduler.Default) //Works with ThrottleSubsequent2, fails with ThrottleSubsequent
.Subscribe(i => Console.WriteLine(i));
}
static int counter = 0;
public IObservable<int> UglyRange(int limit)
{
var uglySource = Observable.Create<int>(o =>
{
if (counter++ == 0)
{
Console.WriteLine("Ugly observable should only be created once.");
Enumerable.Range(1, limit).ToList().ForEach(i => o.OnNext(i));
}
else
{
Console.WriteLine($"Ugly observable should only be created once. This is the {counter}th time created.");
o.OnError(new Exception($"observable invoked {counter} times."));
}
return Disposable.Empty;
});
return uglySource;
}
I'm trying to use Reactive Extensions (Rx) for a task where it seems to be a good fit, polling at a specific interval a web service and display its last x results.
I have a web service that sends me the status of an instrument I want to monitor. I would like to poll this instrument at a specific rate and display in a list the last 20 status that have been polled.
So my list would be like a "moving window" of the service result.
I'm developing a WPF app with Caliburn.Micro, but I don't think this is very relevant.
What I managed to get until now is the following (just a sample app that I hacked quickly, I'm not going to do this in the ShellViewModel in the real app):
public class ShellViewModel : Caliburn.Micro.PropertyChangedBase, IShell
{
private ObservableCollection<string> times;
private string currentTime;
public ShellViewModel()
{
times = new ObservableCollection<string>();
Observable
.Interval(TimeSpan.FromSeconds(1))
.SelectMany(x => this.GetCurrentDate().ToObservable())
.ObserveOnDispatcher()
.Subscribe(x =>
{
this.CurrentTime = x;
this.times.Add(x);
});
}
public IEnumerable<string> Times
{
get
{
return this.times;
}
}
public string CurrentTime
{
get
{
return this.currentTime;
}
set
{
this.currentTime = value;
this.NotifyOfPropertyChange(() => this.CurrentTime);
}
}
private async Task<string> GetCurrentDate()
{
var client = new RestClient("http://www.timeapi.org");
var request = new RestRequest("/utc/now.json");
var response = await client.ExecuteGetTaskAsync(request);
return response.Content;
}
}
In the view I have just a label bound to the CurrentTime property and a list bound to the Times property.
The issue I have is:
It's not limited to the 20 items in the list as I always add items to the ObservableCollection but I can't find a better way to databind
The Interval doesn't work as I'd like. If the querying takes more than 1 second to run, two queries will be run in parallel, which I'd like not to happen. My goal would be that the query repeats indefinitely but at a pace of no more than 1 query every seconds. If a query makes more than 1 second to end, it should wait for it to have finish and directly trigger the new query.
Second edit:
Previous edit below was me being stupid and very confused, it triggers events continuously because Interval is something continuous that never ends. Brandon's solution is correct and works as expected.
Edit:
Based on Brandon's example, I tried to do the following code in LinqPad:
Observable
.Merge(Observable.Interval(TimeSpan.FromSeconds(2)), Observable.Interval(TimeSpan.FromSeconds(10)))
.Repeat()
.Scan(new List<double>(), (list, item) => { list.Add(item); return list; })
.Subscribe(x => Console.Out.WriteLine(x))
And I can see that the write to the console occurs every 2 seconds, and not every 10. So the Repeat doesn't wait for both Observable to be finished before repeating.
Try this:
// timer that completes after 1 second
var intervalTimer = Observable
.Empty<string>()
.Delay(TimeSpan.FromSeconds(1));
// queries one time whenever subscribed
var query = Observable.FromAsync(GetCurrentDate);
// query + interval timer which completes
// only after both the query and the timer
// have expired
var intervalQuery = Observable.Merge(query, intervalTimer);
// Re-issue the query whenever intervalQuery completes
var queryLoop = intervalQuery.Repeat();
// Keep the 20 most recent results
// Note. Use an immutable list for this
// https://www.nuget.org/packages/microsoft.bcl.immutable
// otherwise you will have problems with
// the list changing while an observer
// is still observing it.
var recentResults = queryLoop.Scan(
ImmutableList.Create<string>(), // starts off empty
(acc, item) =>
{
acc = acc.Add(item);
if (acc.Count > 20)
{
acc = acc.RemoveAt(0);
}
return acc;
});
// store the results
recentResults
.ObserveOnDispatcher()
.Subscribe(items =>
{
this.CurrentTime = items[0];
this.RecentItems = items;
});
This should skip the interval messages while a GetCurrentDate is in Progress.
Observable
.Interval(TimeSpan.FromSeconds(1))
.GroupByUntil(p => 1,p => GetCurrentDate().ToObservable().Do(x => {
this.CurrentTime = x;
this.times.Add(x);
}))
.SelectMany(p => p.LastAsync())
.Subscribe();
I'm trying to solve the following:
a) subscriber receives events from IObservable for some time. Then it unsubscribes, do some stuff and then subscribe again. Here it should start receiving events from exactly the same point where unsubscription was performed.
b) Such behavior is desirable for multiple subscribers model. E.g. when one has unsubscribed, others should continue receiving events.
Are there any suggestions from the RX side?
Thanks in advance!
Here's a reasonably simple Rx way to do what you want copied from my answer to this other question. I've created an extension method called Pausable that takes a source observable and a second observable of boolean that pauses or resumes the observable.
public static IObservable<T> Pausable<T>(
this IObservable<T> source,
IObservable<bool> pauser)
{
return Observable.Create<T>(o =>
{
var paused = new SerialDisposable();
var subscription = Observable.Publish(source, ps =>
{
var values = new ReplaySubject<T>();
Func<bool, IObservable<T>> switcher = b =>
{
if (b)
{
values.Dispose();
values = new ReplaySubject<T>();
paused.Disposable = ps.Subscribe(values);
return Observable.Empty<T>();
}
else
{
return values.Concat(ps);
}
};
return pauser.StartWith(false).DistinctUntilChanged()
.Select(p => switcher(p))
.Switch();
}).Subscribe(o);
return new CompositeDisposable(subscription, paused);
});
}
It can be used like this:
var xs = Observable.Generate(
0,
x => x < 100,
x => x + 1,
x => x,
x => TimeSpan.FromSeconds(0.1));
var bs = new Subject<bool>();
var pxs = xs.Pausable(bs);
pxs.Subscribe(x => { /* Do stuff */ });
Thread.Sleep(500);
bs.OnNext(true);
Thread.Sleep(5000);
bs.OnNext(false);
Thread.Sleep(500);
bs.OnNext(true);
Thread.Sleep(5000);
bs.OnNext(false);
It sounds like you need a "pausable" stream. Assuming that only 1 subscriber will handle the values at a time (while the other subscribers just wait), this solution is probably what you need.