While you can reuse WithCancellation
for both cancellations and timeouts I think it’s an overkill for what you need.
A simpler and clearer solution for an async
operation timeout would be to await
both the actual operation and a timeout task using Task.WhenAny
. If the timeout task completes first, you got yourself a timeout. Otherwise, the operation completed successfully:
public static async Task<TResult> WithTimeout<TResult>(this Task<TResult> task, TimeSpan timeout)
{
if (task == await Task.WhenAny(task, Task.Delay(timeout)))
{
return await task;
}
throw new TimeoutException();
}
Usage:
try
{
await DoStuffAsync().WithTimeout(TimeSpan.FromSeconds(5));
}
catch (TimeoutException)
{
// Handle timeout.
}
If you prefer to not throw an exception (as I do) it’s even simpler, just return the default value:
public static Task<TResult> WithTimeout<TResult>(this Task<TResult> task, TimeSpan timeout)
{
var timeoutTask = Task.Delay(timeout).ContinueWith(_ => default(TResult), TaskContinuationOptions.ExecuteSynchronously);
return Task.WhenAny(task, timeoutTask).Unwrap();
}