Asynchronously wait for a task to complete and do some async action while waiting 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&lt;bool&gt; 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), () =&gt;
{
    Console.WriteLine("Waiting...");
    return Task.FromResult(true);
})
.Wait();
public delegate Task AsyncAction();


Instead of creating a custom delegate type, you could just reuse Func&lt;Task&gt; (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.