So, task.Wait()
can be transformed to await task
. The semantics are different, of course, but this is roughly how I would go about transforming a blocking code with Waits
to an asynchronous code with awaits
.
My question is how to transform task.Wait(CancellationToken)
to the respective await
statement?
To create a new Task
that represents an existing task but with an additional cancellation token is quite straightforward. You only need to call ContinueWith
on the task, use the new token, and propagate the result/exceptions in the body of the continuation.
public static Task WithCancellation(this Task task,
CancellationToken token)
{
return task.ContinueWith(t => t.GetAwaiter().GetResult(), token);
}
public static Task<T> WithCancellation<T>(this Task<T> task,
CancellationToken token)
{
return task.ContinueWith(t => t.GetAwaiter().GetResult(), token);
}
This allows you to write task.WithCancellation(cancellationToken)
to add a token to a task, which you can then await
.
await
is used for asynchronous methods/delegates, which either accept a CancellationToken
and so you should pass one when you call it (i.e. await Task.Delay(1000, cancellationToken)
), or they don't and they can't really be canceled (e.g. waiting for an I/O result).
What you can do however, is abandon* these kinds of tasks with this extension method:
public static Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
return task.IsCompleted // fast-path optimization
? task
: task.ContinueWith(
completedTask => completedTask.GetAwaiter().GetResult(),
cancellationToken,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}
Usage:
await task.WithCancellation(cancellationToken);
* The abandoned task doesn't get cancelled, but your code behaves as though it has. It either ends with a result/exception or it will stay alive forever.