0

I have somewhat of a special need / requirement for an observable stream that goes a bit beyond normal throttling and I am not entirely sure how to do it:

Basically I have an observable Stream originally from a normal event like so:

var someEventObservable = Observable.FromEventPattern<SomeEventHandler, SomeEventArgs>(
    handler => this.ColumnWidthChanged += handler,
    handler => this.ColumnWidthChanged -= handler)
    .Select(_ => Unit.Default);

Now as these events can happen in rapid succession and I only need to know whether it happened at least once within a given time frame, I would normally use .Throttle(), i.e. like so:

var someThrottledEventObservable = someEventObservable
    .Throttle(TimeSpan.FromMilliseconds(300));

But my actual requirements go one step further: if an event was raised within that throttling TimeSpan / dueTime AND if yet another event is raised after the first event but still within that dueTime, I want the throttled stream to start over from 0 wait time again and wait another 300ms... and if another event was raised, re-start/extend that time again.. and so on and so on. Only if no other event was raised within the original or restarted TimeSpan(s)/ dueTime the someThrottledEventObservable should yield a new Unit instance.

I hope that makes sense - but basically I want/need a throttled stream of events that yields one event whenever the source stream has stopped yielding new events for a given time & if new events happen within that wait time the throttled stream should re-start waiting.

Or: In an ongoing 'storm' of events .Throttle() alone results in a new Unit every 300ms (in the example above) but I want exactly one new Unit whenever one or more events were fired but no new ones occurred within a 300ms coold-down-period thereafter.

How would I do that?

Jörg Battermann
  • 3,906
  • 4
  • 38
  • 72
  • 2
    Not familiar with rx.net semantics, but it sounds like Throttle is behaving like [sample](http://reactivex.io/documentation/operators/sample.html) rather than [debounce](http://reactivex.io/documentation/operators/debounce.html), which reactivex.io claims to be the operation at play. Debounce _should_ work like you've described (based on the documentation), and a fair rephrase of your question might be "how do I debounce in Reactive Extensions for .NET". – maxwellb Sep 21 '17 at 20:57
  • 2
    I think Throttle is already doing what you expect. It only emits the last event if there is no other event after the designated period. – nikoniko Sep 22 '17 at 03:44
  • @nikoniko yeah - I must be doing something wrong here.. not sure why, but after re-reading the documentation it clearly states that it should/does behave as I want it to.. apparently something else is causing events to trickle through / one is handled multiple times etc.. – Jörg Battermann Sep 22 '17 at 08:45
  • I've posted a new follow-up question because the observed behaviour is indeed a bit odd / different than what I'd expect: https://stackoverflow.com/questions/46361358/rx-net-multiple-mergerd-observables-throttle-not-working-as-expected – Jörg Battermann Sep 22 '17 at 09:31

1 Answers1

0

As @nikoniko already mentioned, throttle will do the trick.

using System;
using System.Reactive.Linq;

namespace Printing {
class Program {
    static void Main(string[] args) {
        var source = Observable.Interval(TimeSpan.FromMilliseconds(333))
            .Do(i => Console.WriteLine($"new item: {i}"));
        var sampling = source.Throttle(TimeSpan.FromSeconds(1))
            .Do(i => Console.WriteLine($"sampled: {i}"));

        var subscription = sampling.Subscribe();

        Console.ReadLine();

        subscription.Dispose();

        Console.ReadLine();
    }
}

} Resulting in nothing because the events from source arrive in two high frequency. But if source need more time to deliver an element then the timespan given in throttle:

using System;
using System.Reactive.Linq;

namespace Printing {
    class Program {
        static void Main(string[] args) {
            var source = Observable.Interval(TimeSpan.FromSeconds(1.2))
                .Do(i => Console.WriteLine($"{DateTime.Now.ToShortTimeString()}: new item: {i}"));
            var sampling = source.Throttle(TimeSpan.FromSeconds(1))
                .Do(i => Console.WriteLine($"{DateTime.Now.ToShortTimeString()}:  {i}"));

            var subscription = sampling.Subscribe();

            Console.ReadLine();

            subscription.Dispose();

            Console.ReadLine();
        }
    }
}

The result will appear after throttling time is over. As you can see, on second after a event in source is fired, it will appear in the result.

08:32:26: new item: 0
08:32:27: throttle 0
08:32:28: new item: 1
08:32:29: throttle 1
08:32:30: new item: 2
08:32:31: throttle 2
08:32:32: new item: 3
08:32:33: throttle 3
08:32:34: new item: 4
08:32:35: throttle 4
08:32:36: new item: 5
08:32:37: throttle 5
0xBADF00D
  • 890
  • 1
  • 11
  • 23