A lightweight, high-performance async debouncer for .NET with typed return values and cancellation token support. Ideal for Blazor, WPF, MAUI, and other async scenarios where you need to debounce API calls, search inputs, or other async operations.
$ dotnet add package AnoiAsyncDebouncerA lightweight, high-performance async debouncer for .NET with typed return values and cancellation token support.
AsyncDebouncer<T> supports any return typeAsyncDebouncer for void operationsdotnet add package AnoiAsyncDebouncer
Or via Package Manager:
Install-Package AnoiAsyncDebouncer
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);
// For operations without return values
var debouncer = new AsyncDebouncer();
await debouncer.DebounceAsync(async cancellationToken =>
{
await SaveDataAsync(cancellationToken);
}, delayMilliseconds: 500);
@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();
}
AsyncDebouncer<T>| Method | Description |
|---|---|
DebounceAsync(operation, delayMs) | Debounce an async operation. Throws TaskCanceledException if superseded. |
DebounceAsync(operation, delayMs, defaultValue) | 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.
The cancellation token passed to your operation is automatically cancelled when:
Cancel() is calledDispose() is calledawait debouncer.DebounceAsync(async ct =>
{
// Check cancellation in long-running operations
ct.ThrowIfCancellationRequested();
// Or pass it to async methods
await LongRunningOperationAsync(ct);
return result;
}, 300);
// Execute any pending operation immediately (e.g., before form submit)
var result = await debouncer.FlushAsync();
// Returns empty list instead of throwing when cancelled
var results = await debouncer.DebounceAsync(
async ct => await FetchDataAsync(ct),
delayMilliseconds: 300,
defaultValue: new List<Item>()
);
IDisposable in your components and dispose the debouncerdefaultValue overload in UI scenarios to avoid exception handlingThis project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.