AnoiAsyncDebouncer 1.1.0

dotnet add package AnoiAsyncDebouncer --version 1.1.0
                    
NuGet\Install-Package AnoiAsyncDebouncer -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AnoiAsyncDebouncer" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AnoiAsyncDebouncer" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="AnoiAsyncDebouncer" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AnoiAsyncDebouncer --version 1.1.0
                    
#r "nuget: AnoiAsyncDebouncer, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AnoiAsyncDebouncer@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AnoiAsyncDebouncer&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=AnoiAsyncDebouncer&version=1.1.0
                    
Install as a Cake Tool

AnoiAsyncDebouncer

A lightweight, high-performance async debouncer for .NET with typed return values and cancellation token support.

Features

  • Typed return values - Generic AsyncDebouncer<T> supports any return type
  • Cancellation support - Proper cancellation token propagation
  • Thread-safe - Safe for concurrent access
  • Flush support - Execute pending operations immediately
  • Max wait time - Optional cap on total debounce duration to prevent starvation under sustained input
  • Memory efficient - Uses object pooling for CancellationTokenSource instances
  • Zero dependencies - Only depends on .NET Standard 2.1
  • Non-generic version - AsyncDebouncer for void operations

Installation

dotnet add package AnoiAsyncDebouncer

Or via Package Manager:

Install-Package AnoiAsyncDebouncer

Quick Start

Basic Usage (with return value)

using AnoiAsyncDebouncer;

// Create a debouncer for string results
var debouncer = new AsyncDebouncer<string>();

// Debounce an async operation with 300ms delay
var result = await debouncer.DebounceAsync(async cancellationToken =>
{
    return await SearchApiAsync(searchTerm, cancellationToken);
}, delayMilliseconds: 300);

Void Operations

// For operations without return values
var debouncer = new AsyncDebouncer();

await debouncer.DebounceAsync(async cancellationToken =>
{
    await SaveDataAsync(cancellationToken);
}, delayMilliseconds: 500);

Blazor Search Example

@inject HttpClient Http
@implements IDisposable

<input @oninput="OnSearchInput" placeholder="Search..." />

@code {
    private readonly AsyncDebouncer<List<string>> _searchDebouncer = new();
    private List<string> searchResults = new();

    private async Task OnSearchInput(ChangeEventArgs e)
    {
        var searchTerm = e.Value?.ToString() ?? "";
        
        searchResults = await _searchDebouncer.DebounceAsync(async ct =>
        {
            if (string.IsNullOrWhiteSpace(searchTerm))
                return new List<string>();
                
            return await Http.GetFromJsonAsync<List<string>>(
                $"api/search?q={searchTerm}", ct) ?? new List<string>();
        }, 300, defaultValue: new List<string>());
        
        StateHasChanged();
    }

    public void Dispose() => _searchDebouncer.Dispose();
}

API Reference

AsyncDebouncer<T>

Method Description
DebounceAsync(operation, delayMs, maxWaitTime?) Debounce an async operation. Throws TaskCanceledException if superseded. Optional maxWaitTime forces execution when debouncing has lasted longer than the specified TimeSpan.
DebounceAsync(operation, delayMs, defaultValue, maxWaitTime?) Debounce with a default value returned on cancellation (no exception).
FlushAsync() Execute pending operation immediately. Returns default(T) if none pending.
Cancel() Cancel any pending operation.
Dispose() Clean up resources and cancel pending operations.

AsyncDebouncer (non-generic)

Same API as above but for void operations. Does not throw on cancellation.

Advanced Usage

Cancellation Support

The cancellation token passed to your operation is automatically cancelled when:

  • A new debounce call supersedes the current one
  • Cancel() is called
  • Dispose() is called
await debouncer.DebounceAsync(async ct =>
{
    // Check cancellation in long-running operations
    ct.ThrowIfCancellationRequested();
    
    // Or pass it to async methods
    await LongRunningOperationAsync(ct);
    
    return result;
}, 300);

Flush Pending Operations

// Execute any pending operation immediately (e.g., before form submit)
var result = await debouncer.FlushAsync();

Max Wait Time (Prevent Starvation)

When a debouncer is continuously called (e.g., a stream of rapid events), normal debouncing will keep resetting the timer and the operation may never execute. The optional maxWaitTime parameter sets an upper bound on how long debouncing can continue before the operation is forced to execute.

// Force execution if debouncing has lasted more than 2 seconds,
// even if new calls keep arriving
var result = await debouncer.DebounceAsync(async ct =>
{
    return await RefreshScreenAsync(ct);
}, delayMilliseconds: 300, maxWaitTime: TimeSpan.FromSeconds(2));

This is especially useful for UI scenarios such as screen redraws or live previews where you want to wait for a pause in activity, but also guarantee the user sees an update within a reasonable time.

// Non-generic version works the same way
var debouncer = new AsyncDebouncer();

await debouncer.DebounceAsync(async ct =>
{
    await RenderPreviewAsync(ct);
}, delayMilliseconds: 500, maxWaitTime: TimeSpan.FromSeconds(3));

If maxWaitTime is not specified (or null), the debouncer behaves exactly as before — it will keep resetting the timer on every call.

Default Values on Cancellation

// Returns empty list instead of throwing when cancelled
var results = await debouncer.DebounceAsync(
    async ct => await FetchDataAsync(ct),
    delayMilliseconds: 300,
    defaultValue: new List<Item>()
);

Best Practices

  1. Always dispose - Implement IDisposable in your components and dispose the debouncer
  2. Use cancellation tokens - Pass the token to your async operations for proper cancellation
  3. Consider default values - Use the defaultValue overload in UI scenarios to avoid exception handling
  4. One debouncer per operation type - Create separate debouncers for different operations

Requirements

  • .NET Standard 2.1 or higher
  • Compatible with: .NET Core 3.0+, .NET 5+, .NET 6+, .NET 7+, .NET 8+

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

Contributions are welcome! Please feel to submit a Pull Request.

Repository

https://github.com/eeaquino/AnoiAsyncDebouncer

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.1

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 152 4/9/2026
1.0.0 163 1/24/2026