I have interactive task which in "worst" scenario is not executed at all, thus it is represented by TaskCompletionSource
.
I would like to wait for either this task completes, or token which I received is cancelled -- whichever happens first. Perfect tool for such job would be Task.WhenAny
, the only problem is it takes only tasks, and I have one Task
and one CancellationToken
.
How to wait (asynchronously, like Task.WhenAny
) for the first event triggered -- completed task, or cancelled token?
async Task MyCodeAsync(CancellationToken token)
{
var tcs = new TaskCompletionSource<UserData>(); // represents interactive part
await Task.WhenAny(tcs.Task, token); // imaginary call
UserData data = tcs.Task.Result; // user interacted, let's continue
...
}
I don't create/manage token, so I cannot change it. I have to deal with it.
Update: For such particular case one could use Register
method on token to cancel the TaskCompletionSource
. For more general method please see Matthew Watson answer.
Here is an extension method that transforms a CancellationToken
to a Task
or Task<TResult>
. The returned task will complete as cancelled immediately after the CancellationToken
receives a cancellation request.
static class CancellationTokenExtensions
{
public static Task AsTask(this CancellationToken token)
{
return new Task(() => throw new InvalidOperationException(), token);
}
public static Task<TResult> AsTask<TResult>(this CancellationToken token)
{
return new Task<TResult>(() => throw new InvalidOperationException(), token);
}
}
Usage example. Just await
any task:
await Task.WhenAny(tcs.Task, token.AsTask());
...or await
and get the result in the same line as well:
var data = await Task.WhenAny(tcs.Task, token.AsTask<UserData>()).Unwrap();
The InvalidOperationException
is thrown just in case, to ensure that the task of the CancellationToken
will never run to completion. Its Status
can only be Created
, Canceled
or Faulted
.
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加