Skip to content

Asynchronous action execution

reisenberger edited this page Aug 31, 2018 · 14 revisions

Asynchronous action execution

For an overview of syntax and policies available, read first the readme: https://github.com/App-vNext/Polly#asynchronous-support. This article describes the asynchronous operation of policies in detail.

Polly fully supports asynchronous executions, using the asynchronous methods:

  • RetryAsync
  • WaitAndRetryAsync
  • CircuitBreakerAsync
  • (etc)
  • ExecuteAsync
  • ExecuteAndCaptureAsync

in place of their synchronous counterparts Retry, WaitAndRetry etc.

A policy instance can be defined for either sync or async execution but not both. You must use a synchronous execution overload (eg Execute) with synchronous-defined policies (eg Retry); and asynchronous execution overloads (eg ExecuteAsync) with asynchronous-defined policies (eg RetryAsync).

Async / await

In asynchronous execution, all delegates are async and run with await.

SynchronizationContext

As recommended for libraries offering an async API, by default, async continuations (when a method resumes from an await) are not run on a captured synchronization context.

If you require the whole .ExecuteAsync(…) call to continue on the captured synchronization context, use the overloads taking a bool continueOnCapturedContext, and set this to true.

Cancellation support

Using an .ExecuteAsync(…) (or similar) method taking a CancellationToken allows async action execution to be cancelled. Cancellation can occur:

  • Before the delegate is actioned at all: In common with the Base Class Library implementation in Task.Run(…) and elsewhere, if the cancellation token is cancelled before execution begins, the delegate is not executed at all.
  • During delegate execution: The action delegates taken by the relevant .ExecuteAsync(…) overloads take a CancellationToken input parameter. The CancellationToken passed into .ExecuteAsync(…) call is passed in turn as the CancellationToken input parameter to the executed delegate, to support cancellation during delegate execution.
  • During any wait operations policies carry out: for example, waits between retries; waits for a bulkhead execution slot.

All cancellation throws the usual OperationCanceledException.

Use async policies consistently for async execution

If you erroneously combine a synchronous policy with an asynchronous execute overload as below (or vice versa):

// Synchronous policy
var policy = Policy
    .Handle<Exception>()
    .Retry(3); 

// Asynchronous execute overload
var something = await policy.ExecuteAsync(async () => await DoSomethingAsync());

then Polly will throw InvalidOperationException.

Avoid compiler gotchas mixing sync and async execution

Avoid:

// Synchronous policy
var policy = Policy
    .Handle<Exception>()
    .Retry(3); 

// NB Bad code: synchronous execution of an async delegate!
var something = await policy.Execute(async () => await DoSomethingAsync());

The compiler will permit the above code to compile, because async is not strictly part of a method signature; the code resolves to compiling a delegate which happens to return Task<T>. Polly additionally cannot throw for any sync/async mismatch, because both the policy (Retry) and the execute overload (Execute) are synchronous.

Note however that the Polly policy will not govern the full asynchronous execution lifecycle of DoSomethingAsync() with such code.

All .NET async methods synchronously return a Task representing the ongoing execution, when execution hits the first await statement. When an async delegate such as async () => await DoSomethingAsync() is run through a synchronous policy.Execute(...), the policy only governs that synchronous execution up to the first await statement returning the Task.

The asynchronous task continues to execute and may throw, placing the error into the returned Task. However, the code is then awaiting that Task (await policy.Execute(...)) outside the policy execution, as synchronous policy execution has already successfully completed synchronously returning the Task. As the code is then awaiting a faulted Task outside the policy execution, the exception will be rethrown without the policy governing it.

Clone this wiki locally