Let's say I have a method that returns an object of type ValueTask, for example the WaitToReadAsync method on ChannelReader. There are three ways I can consume this ValueTask. I can either:
- Directly await it.
- Turn it into a Task, then await it (with
.AsTask()) - Query the
.Resultproperty if it has completed synchronously (if.IsCompletedistrue).
With regards to the third method (calling .Result on the ValueTask), the MSDN documentation states that this property can only be queried once. But, does this property need to be queried at all? In other words, is it okay to never query the result of a ValueTask?
Here is an example of what I mean:
var channel = Channel.CreateUnbounded<int>();
var waitToReadValueTask = channel.Reader.WaitToReadAsync(default);
if (waitToReadValueTask.IsCompleted)
{
if (channel.Reader.TryRead(out var result))
{
// Do something with the result object.
}
}
In this example, the waitToReadValueTask was never consumed. It was never awaited, neither was the .Result property called on it. Is this okay?
Good question. Yes, it is perfectly OK to omit querying the
Resultproperty of aValueTask<T>, provided that you are not interested about the result, or about theExceptionthat might be stored inside the task. It is also OK to ignore entirely aValueTaskafter its creation, if you so wish, resulting in a so-called fire-and-forget task. The task will still complete, even if you haven't kept any reference of it.In general it is a good idea to consume your
ValueTasks though, either byawaiting them or byAsTasking them or by querying theirResult(provided that they are already completed). By consuming aValueTaskyou signal to the underlying implementation that the backingIValueTaskSource<T>instance can be reused. ReusingIValueTaskSource<T>instances is how theValueTask-based APIs are avoiding memory allocations, when the asynchronous method cannot complete synchronously.In your example you are querying the
IsCompletedproperty immediately after creating theValueTask, so it is highly unlikely that theValueTaskwas not completed upon creation. So regarding your example the above advice has only theoretical value.ValueTasks that are completed upon creation are not backed byIValueTaskSource<T>instances, so there is no performance implication by not querying theResultin your example. The advice would be of practical value if you queried theIsCompletedproperty at a later time after creating theValueTask.