Problem
I have a long-running task. My goal is to create a method that will allow me to:
- Asynchronously wait for this task to complete
- While waiting on a task, do some async action once in a while. This
‘action’ basically tells some remote service that task is not dead
and still executing.
I’ve written the following code to solve this problem. Please have a look and share your thoughts!
public delegate Task AsyncAction();
public static class TaskExtensions
{
public static async Task<bool> TimeoutAfter(this Task task, TimeSpan timeout)
{
var timeoutCancellationTokenSource = new CancellationTokenSource();
var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token)).ConfigureAwait(false);
if (completedTask == task)
{
timeoutCancellationTokenSource.Cancel();
await task.ConfigureAwait(false);
return true;
}
else
{
return false;
}
}
public static async Task AwaitWithTimeoutCallback(this Task task, TimeSpan timeout, AsyncAction onTimeout)
{
while (true)
{
var completed = await task.TimeoutAfter(timeout).ConfigureAwait(false);
if (completed)
{
break;
}
await onTimeout().ConfigureAwait(false);
}
await task.ConfigureAwait(false);
}
}
Usage:
var task = Task.Delay(TimeSpan.FromSeconds(15));
task.AwaitWithTimeoutCallback(TimeSpan.FromSeconds(5), () =>
{
Console.WriteLine("Waiting...");
return Task.FromResult(true);
})
.Wait();
Solution
public delegate Task AsyncAction();
Instead of creating a custom delegate type, you could just reuse Func<Task>
(even though logically, it is an action and not a function).
return Task.FromResult(true);
Consider adding an overload that takes a non-async onTimeout
delegate, so that this confusing code wasn’t required.
Or, if you do something like this often, have some TaskEx.Completed
, or something like that.
Otherwise, good job. I especially like that you remembered to use ConfigureAwait(false)
everywhere.