Reputation: 129
I'm implementing retry handling for calls to Azure using the Topaz block. I've implemented a custom exponential backoff retry strategy. However, I'm failing to understand the difference between this:
var result = await RetryPolicy.ExecuteAsync(async () => await SomeAsyncOperation());
vs
var result = await RetryPolicy.ExecuteAsync( () => SomeAsyncOperation());
RetryPolicy.ExecuteAsync according to the documentation will repeatedly execute the async task until the retrypolicy is satisfied, so the first solution seems correct as it awaits the result of the async operation. However, I've tried both ways and they seem to work similarly. I've also seen examples like this one and this one for the second case. Which one is correct?
Upvotes: 2
Views: 1066
Reputation: 12533
This is a very common phenomenon of introducing redundant awaits in your code.
Example : in short we are looking at something like this .
public Task ExecuteAsync(Func<Task> taskAction)
{
return taskAction();
}
(1) await ExecuteAsync( () => Task.Delay(5000));
vs :
(2) await ExecuteAsync( async () => await Task.Delay(5000));
in (1) you are awaiting the task created by the delegate you passed to the retry policy.
in (2) you are awaiting a different task, a redundant one at that, your inner await waits for the task you awaited in (1) a different task is created for you to await when calling ExecuteAsync.
It works because your code acts the same, it still awaits the task returned by ExecuteAsync. It just does some more heavy lifting along the way by creating an additional task (for no good reason).
The moral here is to await the task you intended to.
In Addition :
There are allot of defensive redundant await's out there, but also some that are crucial and seem like their redundant.
For example :
take this read operation where the connection should not be disposed until the asynchronous (for exmpale I/O) operation is done.
await DoSomeReadingAsync();
public async Task DoSomeReadingAsync()
{
using(var connection = new Connection())
{
await SomeReadOperationAsync(connection);
}
}
if you returned the inner task without awaiting it here, it will be destorus because the connection will be disposed before the inner asynchronous operation is done.
await DoSomeReadingAsync();
public Task DoSomeReadingAsync()
{
using(var connection = new Connection())
{
return SomeReadOperationAsync(connection);
}
}
Upvotes: 3