How can I use Reactive Extensions to throttle Events using a max window size?

后端 未结 2 1099
暖寄归人
暖寄归人 2021-02-08 15:30

Scenario:

I am building a UI application that gets notifcations from a backend service every few milliseconds. Once I get a new notification i want to u

相关标签:
2条回答
  • 2021-02-08 15:44

    Don't use Observable.Throttle, use Observable.Sample like this, where the TimeSpan gives the desired minimum interval between updates:

    source.Sample(TimeSpan.FromMilliseconds(50))
    
    0 讨论(0)
  • 2021-02-08 15:54

    As James stated, Observable.Sample will give you the latest value yielded. However, it will do so on a timer, and not in accordance to when the first event in the throttle occurred. More importantly, however, is that if your sample time is high (say ten seconds), and your event fires right after a sample is taken, you won't get that new event for almost ten seconds.

    If you need something a little tighter, you'll need to implement your own function. I've taken the liberty of doing so. This code could definitely use some clean up, but I believe it does what you've asked for.

    public static class ObservableEx
    {
        public static IObservable<T> ThrottleMax<T>(this IObservable<T> source, TimeSpan dueTime, TimeSpan maxTime)
        {
            return source.ThrottleMax(dueTime, maxTime, Scheduler.Default);
        }
    
        public static IObservable<T> ThrottleMax<T>(this IObservable<T> source, TimeSpan dueTime, TimeSpan maxTime, IScheduler scheduler)
        {
            return Observable.Create<T>(o =>
            {
                var hasValue = false;
                T value = default(T);
    
                var maxTimeDisposable = new SerialDisposable();
                var dueTimeDisposable = new SerialDisposable();
    
                Action action = () =>
                {
                    if (hasValue)
                    {
                        maxTimeDisposable.Disposable = Disposable.Empty;
                        dueTimeDisposable.Disposable = Disposable.Empty;
                        o.OnNext(value);
                        hasValue = false;
                    }
                };
    
                return source.Subscribe(
                    x =>
                    {
                        if (!hasValue)
                        {
                            maxTimeDisposable.Disposable = scheduler.Schedule(maxTime, action);
                        }
    
                        hasValue = true;
                        value = x;
                        dueTimeDisposable.Disposable = scheduler.Schedule(dueTime, action);
                    },
                    o.OnError,
                    o.OnCompleted
                );
            });
        }
    }
    

    And a few tests...

    [TestClass]
    public class ThrottleMaxTests : ReactiveTest
    {
        [TestMethod]
        public void CanThrottle()
        {
    
            var scheduler = new TestScheduler();
            var results = scheduler.CreateObserver<int>();
    
            var source = scheduler.CreateColdObservable(
                OnNext(100, 1)
                );
    
            var dueTime = TimeSpan.FromTicks(100);
            var maxTime = TimeSpan.FromTicks(250);
    
            source.ThrottleMax(dueTime, maxTime, scheduler)
                .Subscribe(results);
    
            scheduler.AdvanceTo(1000);
    
            results.Messages.AssertEqual(
                OnNext(200, 1)
                );
        }
    
        [TestMethod]
        public void CanThrottleWithMaximumInterval()
        {
    
            var scheduler = new TestScheduler();
            var results = scheduler.CreateObserver<int>();
    
            var source = scheduler.CreateColdObservable(
                OnNext(100, 1),
                OnNext(175, 2),
                OnNext(250, 3),
                OnNext(325, 4),
                OnNext(400, 5)
                );
    
            var dueTime = TimeSpan.FromTicks(100);
            var maxTime = TimeSpan.FromTicks(250);
    
            source.ThrottleMax(dueTime, maxTime, scheduler)
                .Subscribe(results);
    
            scheduler.AdvanceTo(1000);
    
            results.Messages.AssertEqual(
                OnNext(350, 4),
                OnNext(500, 5)
                );
        }
    
        [TestMethod]
        public void CanThrottleWithoutMaximumIntervalInterferance()
        {
            var scheduler = new TestScheduler();
            var results = scheduler.CreateObserver<int>();
    
            var source = scheduler.CreateColdObservable(
                OnNext(100, 1),
                OnNext(325, 2)
                );
    
            var dueTime = TimeSpan.FromTicks(100);
            var maxTime = TimeSpan.FromTicks(250);
    
            source.ThrottleMax(dueTime, maxTime, scheduler)
                .Subscribe(results);
    
            scheduler.AdvanceTo(1000);
    
            results.Messages.AssertEqual(
                OnNext(200, 1),
                OnNext(425, 2)
                );
        }
    }
    
    0 讨论(0)
提交回复
热议问题