-
Notifications
You must be signed in to change notification settings - Fork 781
Description
The problem
The Observable.Delay overload which has a delayDurationSelector parameter seems to be buggy, because it duplicates the items when the delay is zero (or small enough?). Obviously, the expected behavior is not to duplicate the items.
I am experiencing this wrong behavior both with System.Reactive 3.0.0 and 5.0.0.
Wrong behavior
The wrong behavior occurs in the following examples:
Observable.Range(0, 5)
.Delay(num => Observable.Return(Unit.Default))
.Subscribe(Console.WriteLine);
// prints out duplicated items: 0 0 1 1 2 2 3 3 4 4
Observable.Range(0, 5)
.Delay(num => Observable.FromAsync(async () => {}))
.Subscribe(Console.WriteLine);
// prints out duplicated items: 0 0 1 1 2 2 3 3 4 4
Observable.Range(0, 5)
.Delay(num => Observable.FromAsync(() => Task.CompletedTask))
.Subscribe(Console.WriteLine);
// prints out duplicated items: 0 0 1 1 2 2 3 3 4 4
Observable.Range(0, 5)
.Delay(num => Observable.FromAsync(() => Task.Delay(TimeSpan.Zero)))
.Subscribe(Console.WriteLine);
// prints out duplicated items: 0 0 1 1 2 2 3 3 4 4
Observable.Range(0, 5)
.Delay(num => Observable.FromAsync(async () => Thread.Sleep(TimeSpan.FromMilliseconds(100))))
.Subscribe(Console.WriteLine);
// prints out duplicated items: 0 0 1 1 2 2 3 3 4 4So, the problem is not just the Observable.FromAsync invocation, because a simple Observable.Return can also reproduce the problem.
Also, by looking at the Observable.FromAsync + Thread.Sleep combo, it seems that the problem occurs if there is no real asynchronicity, i.e. when the delayDurationSelector observable fires immediately.
Good behavior
However, the following examples behave as expected, i.e. there is no duplication:
Observable.Range(0, 5)
.Delay(num => Observable.Empty(Unit.Default))
.Subscribe(Console.WriteLine);
// prints out items as expected: 0 1 2 3 4
Observable.Range(0, 5)
.Delay(num => Observable.Defer(() => Observable.Start(() => { })))
.Subscribe(Console.WriteLine);
// prints out items as expected: 0 1 2 3 4
Observable.Range(0, 5)
.Delay(num => Observable.Defer(() => Observable.Start(() => Thread.Sleep(TimeSpan.Zero))))
.Subscribe(Console.WriteLine);
// prints out items as expected: 0 1 2 3 4
Observable.Range(0, 5)
.Delay(num => Observable.FromAsync(() => Task.Delay(TimeSpan.FromMilliseconds(1))))
.Subscribe(Console.WriteLine);
// prints out items as expected (in an arbitrary order): 0 1 2 3 4So, it seems that the synchronous case is okay, and the Observable.Empty as well. If the delay is large enough, then the problem does not occur either, not even in the asynchronous case.
Good behavior for the simpler overload
Note that the simpler Observable.Delay overload with the dueTime parameter does not have this problem:
Observable.Range(0, 5)
.Delay(TimeSpan.Zero)
.Subscribe(Console.WriteLine);
// prints out items as expected: 0 1 2 3 4