Philiprehberger.Polling
0.2.5
dotnet add package Philiprehberger.Polling --version 0.2.5
NuGet\Install-Package Philiprehberger.Polling -Version 0.2.5
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="Philiprehberger.Polling" Version="0.2.5" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Philiprehberger.Polling" Version="0.2.5" />
<PackageReference Include="Philiprehberger.Polling" />
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 Philiprehberger.Polling --version 0.2.5
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: Philiprehberger.Polling, 0.2.5"
#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 Philiprehberger.Polling@0.2.5
#: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=Philiprehberger.Polling&version=0.2.5
#tool nuget:?package=Philiprehberger.Polling&version=0.2.5
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
Philiprehberger.Polling
Poll any async operation until a condition is met with configurable intervals, timeouts, and backoff strategies.
Installation
dotnet add package Philiprehberger.Polling
Usage
using Philiprehberger.Polling;
// Poll an API until the order status is "completed"
var result = await Poll
.Until(
async () => await client.GetOrderStatusAsync(orderId),
status => status == "completed")
.Every(TimeSpan.FromSeconds(2))
.WithTimeout(TimeSpan.FromMinutes(5))
.WithBackoff(BackoffStrategy.Exponential)
.ExecuteAsync();
if (result.Succeeded)
Console.WriteLine($"Order completed after {result.Attempts} attempts");
Side-effect polling
Poll an operation that throws until it succeeds:
var result = await Poll
.Until(async () => await db.PingAsync())
.Every(TimeSpan.FromSeconds(1))
.WithTimeout(TimeSpan.FromSeconds(30))
.ExecuteAsync();
Backoff strategies
// Constant (default) — same interval every attempt
.WithBackoff(BackoffStrategy.Constant)
// Linear — interval grows by base each attempt (500ms, 1s, 1.5s, ...)
.WithBackoff(BackoffStrategy.Linear)
// Exponential — interval doubles each attempt (500ms, 1s, 2s, 4s, ...)
.WithBackoff(BackoffStrategy.Exponential)
// Exponential with jitter — exponential + random jitter to avoid thundering herd
.WithBackoff(BackoffStrategy.ExponentialWithJitter)
Max Attempts
Limit polling to a fixed number of attempts, independent of timeout:
var result = await Poll
.Until(
async () => await client.GetJobStatusAsync(jobId),
status => status == "done")
.Every(TimeSpan.FromSeconds(1))
.WithMaxAttempts(10)
.ExecuteAsync();
if (!result.Succeeded)
Console.WriteLine($"Gave up after {result.Attempts} attempts");
Polling Context
Use a context-aware predicate to access attempt metadata:
var result = await Poll
.Until(
async () => await GetValueAsync(),
(value, context) =>
{
Console.WriteLine($"Attempt {context.AttemptNumber}, elapsed {context.Elapsed}");
return value > 100;
})
.Every(TimeSpan.FromSeconds(1))
.ExecuteAsync();
The PollContext provides AttemptNumber (1-based), Elapsed time, and LastException.
Exception Filtering
Only retry on specific exception types; other exceptions propagate immediately:
var result = await Poll
.Until(async () => await db.PingAsync())
.Every(TimeSpan.FromSeconds(1))
.OnlyRetryOn<TimeoutException>()
.WithTimeout(TimeSpan.FromSeconds(30))
.ExecuteAsync();
Observability
var result = await Poll
.Until(
async () => await service.GetHealthAsync(),
health => health.IsHealthy)
.Every(TimeSpan.FromSeconds(1))
.OnAttempt((value, attempt) =>
Console.WriteLine($"Attempt {attempt}: healthy={value.IsHealthy}"))
.ExecuteAsync();
Cancellation
using var cts = new CancellationTokenSource();
var result = await Poll
.Until(
async () => await GetValueAsync(),
v => v > 100)
.Every(TimeSpan.FromMilliseconds(200))
.WithCancellation(cts.Token)
.ExecuteAsync();
API
Poll
| Method | Description |
|---|---|
Until<T>(Func<Task<T>>, Func<T, bool>) |
Create a poll builder that checks a predicate against returned values |
Until<T>(Func<Task<T>>, Func<T, PollContext, bool>) |
Create a poll builder with a context-aware predicate |
Until(Func<Task>) |
Create a poll builder for a side-effect operation that succeeds when it stops throwing |
PollBuilder<T> / PollBuilder
| Method | Description |
|---|---|
Every(TimeSpan) |
Set the base interval between attempts (default 500 ms) |
WithTimeout(TimeSpan) |
Set the maximum total polling duration |
WithMaxAttempts(int) |
Set the maximum number of polling attempts |
Until(Func<T, PollContext, bool>) |
Set a context-aware predicate (PollBuilder<T> only) |
WithBackoff(BackoffStrategy) |
Set the backoff strategy (default Constant) |
OnlyRetryOn<TException>() |
Only retry on the specified exception type |
OnAttempt(Action<T, int>) |
Register a callback after each attempt |
WithCancellation(CancellationToken) |
Provide a cancellation token |
ExecuteAsync() |
Run the poll loop and return a PollResult<T> |
PollContext
| Property | Type | Description |
|---|---|---|
AttemptNumber |
int |
Number of attempts made so far (1-based) |
Elapsed |
TimeSpan |
Total time elapsed since polling started |
LastException |
Exception? |
The last exception encountered, if any |
PollResult<T>
| Property | Type | Description |
|---|---|---|
Succeeded |
bool |
Whether the predicate was satisfied |
Value |
T? |
The last value returned by the operation |
Attempts |
int |
Total number of attempts executed |
Elapsed |
TimeSpan |
Total wall-clock time spent polling |
LastException |
Exception? |
The last exception thrown, if any |
IsTimedOut |
bool |
Whether polling ended due to timeout |
BackoffStrategy
| Value | Description |
|---|---|
Constant |
Same interval every attempt |
Linear |
Interval grows by base each attempt |
Exponential |
Interval doubles each attempt |
ExponentialWithJitter |
Exponential with random jitter |
Development
dotnet build src/Philiprehberger.Polling.csproj --configuration Release
Support
If you find this project useful:
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. 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. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
net8.0
- 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.