Debouncing & Throttling for taming high frequency code execution
Debouncing and Throttling are two common strategies to tame the code execution that handles high frequency events.
Debouncing and Throttling are two common strategies to tame the code execution that handles high frequency events.
Like, for instance, keyboard typing. Or mouse movement. Or animation frame changes. Or resizing. Or scrolling. Or zooming. Or... you get the idea.
So what are they and what's the difference between the two?
Throttling
Means execute not more than once every X seconds (or milliseconds, or any time span) while the high-frequency event is happening. Plus the first, initial call.
stateDiagram-v2 [*] --> A A --> B B --> C C --> D D --> E E --> F F --> [*] [*] --> C :Throttle C --> F :Throttle F --> [*] :Throttle
So, for instance, throttling at 0.5 seconds, means that even if I invoke the execution 42 times in 1 second, it'll actually be executed only 3 times: once every 0.5 seconds + the initial call.
timeline title Throttling @ 0.5 seconds 0s : Normal, initial, Execution Call โ : Throttled, initial, Execution Call โ 0.1s : Normal Execution Call โ : Throttled Execution Call ๐ 0.3s : Normal Execution Call โ : Throttled Execution Call ๐ 0.5s : (No explicit execution call) : Throttled Execution Call โ 0.7s : Normal Execution Call โ : Throttled Execution Call ๐ 0.95s : Normal Execution Call โ : Throttled Execution Call ๐ 1.0s : Normal Execution Call โ : Throttled Execution Call โ
Debouncing
Means don't execute at all while the high-frequency event is happening. Execute only afterwards. Not even the initial call.
stateDiagram-v2 [*] --> A A --> B B --> C C --> D D --> E E --> F F --> [*] [*] --> [*] :Debounce
So, for instance, debouncing at 0.5 seconds, means that even if I invoke the execution 42 times in 1 second, it'll actually be executed exactly 1 time, 0.5 seconds after the high-frequency has stopped, so after 1.5 seconds.
timeline title Debouncing @ 0.5 seconds 0s : Normal, initial, Execution Call โ : Debounced Execution Call ๐ 0.1s : Normal Execution Call โ : Debounced Execution Call ๐ 0.3s : Normal Execution Call โ : Debounced Execution Call ๐ 0.7s : Normal Execution Call โ : Debounced Execution Call ๐ 0.95s : Normal Execution Call โ : Debounced Execution Call ๐ 1.0s : Normal Execution Call โ : Debounced Execution Call ๐ 1.5s : (No explicit execution call) : Debounced Execution Call โ
Usage
dotnet add package H.Necessaire
highFrequencyEventDaemon.OnHighFrequencyEvent += NormalExecution;
highFrequencyEventDaemon.OnHighFrequencyEvent += ThrottledExecution;
highFrequencyEventDaemon.OnHighFrequencyEvent += DebouncedExecution;
await highFrequencyEventDaemon.Start();
StopDaemonAfter(highFrequencyEventDaemon, TimeSpan.FromSeconds(3));
Debounced and Throttled execution setup gist
static async Task NormalExecution(object sender, EventArgs ev)
{
await Console.Out.WriteAsync('_');
}
Normal execution
static readonly Throttler throttler = new Throttler(async () => await Console.Out.WriteAsync("_Tht"), TimeSpan.FromSeconds(.35));
static async Task ThrottledExecution(object sender, EventArgs ev)
{
await throttler.Invoke();
}
Throttled execution
static readonly Debouncer debouncer = new Debouncer(async () => await Console.Out.WriteAsync("_Dbc"), TimeSpan.FromSeconds(.35));
static async Task DebouncedExecution(object sender, EventArgs ev)
{
await debouncer.Invoke();
}
Debounced execution
Running... press Ctrl+C to stop.
__Tht___________________Tht_____________________Tht________________________Tht_______________________Tht________________________Tht______________________Tht________________________Tht_______________________Tht__________Tht_Dbc
Console output