How to throttle the speed of an event without using Rx Framework

This works, if your event is of type EventHandler<EventArgs> for example. It creates a wrapper for your event handler that is throttled:

private EventHandler<EventArgs> CreateThrottledEventHandler(
    EventHandler<EventArgs> handler, 
    TimeSpan throttle)
{   
    bool throttling = false;
    return (s,e) =>
    {
        if(throttling) return;              
        handler(s,e);
        throttling = true;
        Task.Delay(throttle).ContinueWith(_ => throttling = false);
    };
}

Attach like this:

this.SomeEvent += CreateThrottledEventHandler(
    (s,e) => Console.WriteLine("I am throttled!"),
    TimeSpan.FromSeconds(5));

Although, you should store the handler returned from CreateThrottledEventHandler if you need to unwire it with -= later.


I believe the following requirements are essential in a 'throttled' event handler:

  • The first event is raised immediately.
  • Subsequent events - which occur within the throttling period - are ignored.
  • The last event to occur during the throttling period is guaranteed to be raised, once the throttling period has expired.

Considering those requirements, the previously-accepted answer was not satisfactory; it correctly fulfills the first two requirements, but it does not guarantee that the last event will eventually be raised. I think that point is particularly important, because events which are raised with high frequency typically represent 'change of state' and/or 'user requests'; and we always want to receive the last update for changes in state or user interaction.

In an effort to satisfy all these requirements, I created my own generic "ThrottledEventHandler" class.

public class ThrottledEventHandler<TArgs>
    where TArgs : EventArgs
{
    private readonly EventHandler<TArgs> _innerHandler;
    private readonly EventHandler<TArgs> _outerHandler;
    private readonly Timer _throttleTimer;

    private readonly object _throttleLock = new object();
    private Action _delayedHandler = null;

    public ThrottledEventHandler(EventHandler<TArgs> handler, TimeSpan delay)
    {
        _innerHandler = handler;
        _outerHandler = HandleIncomingEvent;
        _throttleTimer = new Timer(delay.TotalMilliseconds);
        _throttleTimer.Elapsed += Timer_Tick;
    }

    private void HandleIncomingEvent(object sender, TArgs args)
    {
        lock (_throttleLock)
        {
            if (_throttleTimer.Enabled)
            {
                _delayedHandler = () => SendEventToHandler(sender, args);
            }
            else
            {
                SendEventToHandler(sender, args);
            }
        }
    }

    private void SendEventToHandler(object sender, TArgs args)
    {
        if (_innerHandler != null)
        {
            _innerHandler(sender, args);
            _throttleTimer.Start();
        }
    }

    private void Timer_Tick(object sender, EventArgs args)
    {
        lock (_throttleLock)
        {
            _throttleTimer.Stop();
            if (_delayedHandler != null)
            {
                _delayedHandler();
                _delayedHandler = null;
            }
        }
    }

    public static implicit operator EventHandler<TArgs>(ThrottledEventHandler<TArgs> throttledHandler)
    {
        return throttledHandler._outerHandler;
    }
}

Usage looks something like this:

myObject.MyFrequentlyRaisedEvent += new ThrottledEventHandler(MyActualEventHandler, TimeSpan.FromMilliseconds(50));