Coder Social home page Coder Social logo

aldaviva / throttledebounce Goto Github PK

View Code? Open in Web Editor NEW
29.0 4.0 5.0 197 KB

๐Ÿš— Rate-limit your actions and funcs by throttling and debouncing them. Retry when an exception is thrown.

Home Page: https://www.nuget.org/packages/ThrottleDebounce/

License: Apache License 2.0

C# 100.00%
rate-limiting debounce throttle retry

throttledebounce's Introduction

ThrottleDebounce icon ThrottleDebounce

Nuget GitHub Workflow Status Coveralls

Rate-limit your actions and funcs by throttling and debouncing them. Retry when an exception is thrown.

This is a .NET library that lets you rate-limit delegates so they are only executed at most once in a given interval, even if they are invoked multiple times in that interval. You can also invoke a delegate and automatically retry it if it fails.

Installation

This package is available on NuGet Gallery.

dotnet add package ThrottleDebounce
Install-Package ThrottleDebounce

It targets .NET Standard 2.0 and .NET Framework 4.5.2, so it should be compatible with many runtimes.

Rate limiting

Usage

Action originalAction;
Func<int> originalFunc;

TimeSpan wait = TimeSpan.FromMilliseconds(50);
using RateLimitedAction throttledAction = Throttler.Throttle(originalAction, wait, leading: true, trailing: true);
using RateLimitedFunc<int> debouncedFunc = Debouncer.Debounce(originalFunc, wait, leading: false, trailing: true);

throttledAction.Invoke();
int? result = debouncedFunc.Invoke();
  1. Call Throttler.Throttle() to throttle your delegate, or Debouncer.Debounce() to debounce it. Pass
    1. Action action/Func func โ€” your delegate to rate-limit
    2. TimeSpan wait โ€” how long to wait between executions
    3. bool leading โ€” true if the first invocation should be executed immediately, or false if it should be queued. Optional, defaults to true for throttling and false for debouncing.
    4. bool trailing โ€” true if subsequent invocations in the waiting period should be enqueued for later execution once the waiting interval is over, or false if they should be discarded. Optional, defaults to true.
  2. Call the resulting RateLimitedAction/RateLimitedFunc object's Invoke() method to enqueue an invocation.
    • RateLimitedFunc.Invoke will return default (e.g. null) if leading is false and the rate-limited Func has not been executed before. Otherwise, it will return the Func's most recent return value.
  3. Your delegate will be executed at the desired rate.
  4. Optionally call the RateLimitedAction/RateLimitedFunc object's Dispose() method to prevent all queued executions from running when you are done.

Understanding throttling and debouncing

Summary

Throttling and debouncing both restrict a function to not execute too often, no matter how frequently you invoke it.

This is useful if the function is invoked very frequently, like whenever the mouse moves, but you don't want to it to run every single time the pointer moves 1 pixel, because the function is expensive, such as rendering a user interface.

Throttling allows the function to still be executed periodically, even with a constant stream of invocations.

Debouncing prevents the function from being executed at all until it hasn't been invoked for a while.

An invocation can result in at most one execution. For example, if both leading and trailing are true, one single invocation will execute once on the leading edge and not on the trailing edge.

Diagram

Strategies for Rate-Limiting

Lodash documentation

Article and demo

Debouncing and Throttling Explained Through Examples by David Corbacho

Examples

Throttle an action to execute at most every 1 second

Action throttled = Throttler.Throttle(() => Console.WriteLine("hi"), TimeSpan.FromSeconds(1)).Invoke;

throttled(); //logs at 0s
throttled(); //logs at 1s
Thread.Sleep(1000);
throttled(); //logs at 2s

Debounce a function to execute after no invocations for 200 milliseconds

Func<double, double, double> debounced = Debouncer.Debounce((double x, double y) => Math.Sqrt(x * x + y * y),
    TimeSpan.FromMilliseconds(200)).Invoke;

double? result;
result = debounced(1, 1); //never runs
result = debounced(2, 2); //never runs
result = debounced(3, 4); //runs at 200ms

Canceling a rate-limited action so any queued executions won't run

RateLimitedAction rateLimited = Throttler.Throttle(() => Console.WriteLine("hello"), TimeSpan.FromSeconds(1));

rateLimited.Invoke(); //runs at 0s
rateLimited.Dispose();
rateLimited.Invoke(); //never runs

Save a WPF window's position to the registry at most every 1 second

static void SaveWindowLocation(double x, double y) => Registry.SetValue(@"HKEY_CURRENT_USER\Software\My Program", 
    "Window Location", $"{x},{y}");

Action<double, double> saveWindowLocationThrottled = Throttler.Throttle<double, double>(saveWindowLocation, 
    TimeSpan.FromSeconds(1)).Invoke;

LocationChanged += (sender, args) => SaveWindowLocationThrottled(Left, Top);

Prevent accidental double-clicks on a WPF button

public MainWindow(){
    InitializeComponent();

    Action<object, RoutedEventArgs> onButtonClickDebounced = Debouncer.Debounce<object, RoutedEventArgs>(
        OnButtonClick, TimeSpan.FromMilliseconds(40), true, false).Invoke;

    MyButton.Click += new RoutedEventHandler(onButtonClickDebounced);
}

private void OnButtonClick(object sender, RoutedEventArgs e) {
    MessageBox.Show("Button clicked");
}

Retrying

Given a function or action, you can execute it and, if it threw an exception, automatically execute it again until it succeeds.

Usage

Retrier.Attempt(attempt => MyErrorProneAction(), maxAttempts: 2);
  1. Call Retrier.Attempt(). Pass
    1. Action<int> action/Func<int, T> func โ€” your delegate to attempt, and possibly retry if it throws exceptions. The attempt number will be passed as the int parameter, starting with 0 before the first attempt. If this func returns a Task, it will be awaited to determine if it threw an exception.
    2. int maxAttempts โ€” the total number of times the delegate is allowed to run in this invocation, equal to 1 initial attempt plus up to maxAttempts - 1 retries if it throws an exception. Must be at least 1, if you pass 0 it will clip to 1. Defaults to 2. For infinite retries, pass null.
    3. Func<int, TimeSpan> delay โ€” how long to wait between attempts, as a function of the attempt number. The upcoming attempt number will be passed as a parameter, starting with 1 before the second attempt. You can return a constant TimeSpan for a fixed delay, or pass longer values for subsequent attempts to implement, for example, exponential backoff. Optional, defaults to null, which means no delay. The minimum value is 0, the maximum value is int.MaxValue (uint.MaxValue - 1 starting in .NET 6), and values outside this range will be clipped.
    4. Func<Exception, bool> isRetryAllowed โ€” whether the delegate is permitted to execute again after a given Exception instance. Return true to allow or false to deny retries. For example, you may want to retry after HTTP 500 errors since subsequent requests may succeed, but stop after the first failure for an HTTP 403 error which probably won't succeed if the same request is sent again. Optional, defaults to retrying on all exceptions besides OutOfMemoryException.
    5. Action beforeRetry/Func<Task> beforeRetry โ€” a delegate to run extra logic between attempts, for example, if you want to log a message or perform any cleanup before the next attempt. Optional, defaults to not running anything between attempts.
    6. CancellationToken cancellationToken โ€” used to cancel the attempts and delays before they have all completed. Optional, defaults to no cancellation token. When cancelled, Attempt() throws a TaskCancelledException.
  2. If your delegate returns a value, it will be returned by Attempt().

Example

Send at most 5 HTTP requests, 2 seconds apart, until a 200 response is received

using HttpClient httpClient = new();
HttpStatusCode statusCode = await Retrier.Attempt(async attempt => {
    Console.WriteLine($"Attempt #{attempt:N0}...");
    HttpResponseMessage response = await httpClient.GetAsync("https://httpbin.org/status/200%2C500");
    Console.WriteLine($"Received response status code {(int) response.StatusCode}.");
    response.EnsureSuccessStatusCode(); // throws HttpRequestException for status codes outside the range [200, 300)
    return response.StatusCode;
}, 5, _ => TimeSpan.FromSeconds(2));
Console.WriteLine($"Final response: {(int) statusCode}")
Attempt #0...
Received response status code 500
Attempt #1...
Received response status code 500
Attempt #2...
Received response status code 200
Final response: 200

throttledebounce's People

Contributors

aldaviva avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

throttledebounce's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.