I’m trying to expand my knowledge on Rx. So I’m just playing around with streams and try to make them behave as I would expect them to do.
While I have read bevor that the Repeat() operator has difficulties in practice because you could loose notifications between the OnCompleted and the re-subscription, I can’t figure out on my own why the following happens.
var subject = new Subject<string>();
var my = subject
.Take(1)
.Merge(Observable.Empty<string>().Delay(TimeSpan.FromMilliseconds(2000)))
.Repeat();
my.Subscribe(Console.WriteLine);
var stopwatch = new Stopwatch();
stopwatch.Start();
Scheduler.ThreadPool.Schedule(TimeSpan.FromSeconds(1), () => subject.OnNext("1 at " + stopwatch.ElapsedMilliseconds));
Scheduler.ThreadPool.Schedule(TimeSpan.FromSeconds(2), () => subject.OnNext("2 at " + stopwatch.ElapsedMilliseconds));
Scheduler.ThreadPool.Schedule(TimeSpan.FromSeconds(3), () => subject.OnNext("3 at " + stopwatch.ElapsedMilliseconds));
Scheduler.ThreadPool.Schedule(TimeSpan.FromSeconds(4), () => subject.OnNext("4 at " + stopwatch.ElapsedMilliseconds));
Scheduler.ThreadPool.Schedule(TimeSpan.FromSeconds(5), () => subject.OnNext("5 at " + stopwatch.ElapsedMilliseconds));
Scheduler.ThreadPool.Schedule(TimeSpan.FromSeconds(6), () => subject.OnNext("6 at " + stopwatch.ElapsedMilliseconds));
Console.ReadLine();
When I run this example the results are totally non-deterministic:
Result 1:
1 at 1006
3 at 3007
5 at 4995
It’s good that it left out the 2 and 4 but even inside this result there is some strangeness because actually between the 3 and the 5 is no real 2 seconds gap.
However, the results can be even worse. See this one:
1 at 1003
2 at 2003
4 at 4005
6 at 6004
There is no 2 seconds gap between 1 and 2. It’s exactly one second. Why didn’t he left it out?
If anyone could clarify things for me, I would be more than happy!
EDIT
I just noticed that it might be the Merge that is wrong here. If I refactor my query to Concat things seem to happen as they should:
var my = subject
.Take(1)
.Concat(Observable.Empty<string>().Delay(TimeSpan.FromMilliseconds(2000)))
.Repeat();
Windows (and other desktop OSes) is not a runtime OS and so you can’t rely that its timers will be precise down to milliseconds. And especially if you have more timers, this can lead to non-deterministic behavior, which is exactly your case.
This is how your original sequence works:
Take(1)subscribes tosubjectsubject, 1 is written out; after this point, nobody subscribes tosubjectanymoreTake(1)subscribes tosubjectagain and another timer for delayed empty observable starts2is added tosubjectBecause of slight differences in timing, the two actions at time approx. 2 can happen in any order. And the order matters, either 2 is added before
Take()resubscribes or before. And thus, 2 can be written out, or not.If what you want is a sequence like this:
then I think the code in your edit is right.
But this in no way guarantees deterministic results. On my computer, if I change the wait time of the delayed empty observable to 1960 ms, I get non-deterministic results when using
Concat().