Why and When use ExecuteAsync or Execute? [what happens when you execute an async delegate through a sync policy]
See original GitHub issueHi guys,
I already read your article explaining synchronous vs asynchronous policies, and I understand your point, but in the end, it looks like there is no difference if I use either ExecuteAsync
or Execute
method, in the below example I’m executing an async method with Sync and Async policies and it works ok in both cases, so what’s the difference, why I would use one or another, I’m confused with the behavior.
private async Task<bool> DoSomehitngAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1));
return true;
}
public async Task TestAsync()
{
var policy = Policy
.Handle<Exception>()
.RetryAsync(3);
await policy.ExecuteAsync(async () =>
{
var something = await DoSomehitngAsync();
Assert.AreEqual(true, something);
});
}
public async void TestSync()
{
var policy = Policy
.Handle<Exception>()
.Retry(3);
await policy.Execute(async () =>
{
var something = await DoSomehitngAsync();
Assert.AreEqual(true, something);
});
}
Issue Analytics
- State:
- Created 5 years ago
- Reactions:3
- Comments:6 (5 by maintainers)
Top Results From Across the Web
Use ExecuteAsync to execute messages asynchronously
You configure the request and pass the request instance as an argument to IOrganizationService.Execute. ExecuteAsyncResponse returns with the ID ...
Read more >Cleanly formalise the separation of sync and async policies ...
You can only Execute() (etc) on synchronous policies; ExecuteAsync() on async policies; but not use one policy instance for both sync and async...
Read more >What is the correct way to call Polly ExecuteAsync method?
1 Answer 1 ... In this simple case, there's no semantic difference. The version eliding async and await has an almost-immeasurable performance ...
Read more >execute() vs executeAsync() in Script Action
As I understand it, executeAsync is asynchronous, but waitForResponse forces it to act synchronously, and if the timer runs out for either ...
Read more >Using Execution Context in Polly
For both sync and async executions, Polly ensures that policy hooks (eg onRetry or onBreak delegates) complete before the next phase of policy ......
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
To answer your original question:
Use async policies and ExecuteAsync(…) any time you are executing an async delegate.
Why? In other words, why does
TestSync()
in the above example fail?At the line marked
// Providing the compiler an async delegate ...
the code provides the compiler anasync
delegate where it expects a sync one. The compiler doesn’t complain. Anasync
modifier isn’t actually intrinsically part of the delegate signature, it doesn’t cause a compile error due to method signature mismatch. All anasync
modifier does is say “the delegate can containawait
statements”. And modifies the return signature of the delegate: in this case fromvoid
toTask
. (An async modifier doesn’t even make a delegate run asynchronously, it just (broadly) does those two previously mentioned things.) For more background on these nuances (if unfamiliar) check out articles/blogs by Stephen Cleary and Stephen Toub. So your whole executedasync () =>
delegate here is - as far as the compiler is concerned - just aFunc<Task>
.So at the line marked
policy.Execute( // Executing a Func<Task> delegate through a sync policy
, the code is actually doingpolicy.Execute<Task>(Func<Task> foo)
. It’s saying: execute that delegate through the policy and give me back theTask
the delegate returns.The final piece of understanding relies on knowing what
await
actually does. When execution hits anawait
statement, it immediately returns from the method synchronously, returning aTask
representing the ongoing work. So what happens when youawait policy.Execute<Task>(Func<Task> foo)
is this:policy
executes theFunc<Task>
that is your delegateawait
, invar something = await DoSomethingAsync();
, the delegate synchronously (see above) returns theTask
representing the rest of the execution.Task
, so as far aspolicy
is concerned, the execution has completed with success.policy
has finished its work (it executed a synchronous delegate and that delegate returned synchronously without an exception yet), sopolicy
returns thatTask
to the calling code and plays no further part in the execution.await
that returnedTask
.Task
throws. The calling code is awaiting a Task that throws, so it ~bombs out~ rethrows.Does that help?
EDIT: To look at this another way, we can look at why it works with the async policy and not with the sync policy.
await
. So it awaits theTask
that represents doing the user-delegate work (after the first await is hit). So it captures the exception thrown atif (++counter <= 2) throw new Exception(...);
. So the rest of the policy can handle that exception.await
. So (when an async delegate is executed through a sync policy) it immediately returns theTask
which represents doing the user-delegate work (after the first await is hit). So it doesn’t govern the exception thrown atif (++counter <= 2) ...
In general the moral of the story is: don’t mix sync and
async
code - particularly where exception-handling is involved.@vany0114 You are seeing no difference in the specific example you posted because the delegates executed through the policy do not fault. Try this code and you’ll see a difference. The key difference is that the executed delegate faults (but few enough times that the policy should handle it). Incidental (not significant) differences are:
Assert.Fail(...)
only to quickly run this up as a console app.TestSync()
toasync Task
so that we couldawait
it properly. (Won’t go into a side discussion here about the perils ofasync void
which you may know anyway.)I’ll post a follow-up post in a moment explaining why
TestSync()
fails (but post back if it’s immediately self-evident to you, on seeing this!)