If an API returns a ValueTask
or ValueTask<T>
, is there a way to perform a ContinueWith
on it, like I'm able to do with Task
? Is there a Microsoft-provided NuGet library for doing so with .NET Standard 2.0?

- 20,268
- 21
- 102
- 205
-
1`ContinueWith` is the legacy API. Frankly, you shouldn't be using it **at all**. You should `await` both tasks and value-tasks. – Marc Gravell Mar 10 '21 at 20:35
3 Answers
Use valueTask.AsTask()
. AsTask()
is an escape hatch for just such use cases as yours. The purpose of ValueTask
is to avoid allocations whenever possible, but if you're going to invoke a continuation on another thread, you will need to allocate something and it might as well be a Task<T>
.

- 19,370
- 5
- 54
- 56
Here you are:
public static async ValueTask ContinueWith<TResult>(this ValueTask<TResult> source,
Action<ValueTask<TResult>> continuationAction)
{
// The source task is consumed after the await, and cannot be used further.
ValueTask<TResult> completed;
try
{
completed = new(await source.ConfigureAwait(false));
}
catch (OperationCanceledException oce)
{
var tcs = new TaskCompletionSource<TResult>();
tcs.SetCanceled(oce.CancellationToken);
completed = new(tcs.Task);
}
catch (Exception ex)
{
completed = new(Task.FromException<TResult>(ex));
}
continuationAction(completed);
}
A simpler implementation would be to Preserve
the ValueTask<TResult>
before the await
, and then invoke the continuationAction
passing the preserved task as argument. This would cause invariably an allocation though, even in the case of the successful completion of the task.
The reason that I am avoiding the one-line Task.FromCanceled
method in the cancellation case, is because this method requires that the supplied CancellationToken
is canceled, otherwise it throws an ArgumentOutOfRangeException
.

- 34,835
- 7
- 69
- 104
I know I am posting it too late. However wanted to share the approach I used...
ValueTask<T> vt = ValueTaskTReturningMethod();
var awt = vt.GetAwaiter();
awt.OnCompleted(() => {
T val = awt.GetResult();
//code you want to execute after async operation completes
}

- 31
- 2
-
Or just `T val = await vt; // code you want to execute...` - much simpler – Marc Gravell Mar 10 '21 at 20:37