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
.Result
property if it has completed synchronously (if.IsCompleted
istrue
).
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
Result
property of aValueTask<T>
, provided that you are not interested about the result, or about theException
that might be stored inside the task. It is also OK to ignore entirely aValueTask
after 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
ValueTask
s though, either byawait
ing them or byAsTask
ing them or by querying theirResult
(provided that they are already completed). By consuming aValueTask
you 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
IsCompleted
property immediately after creating theValueTask
, so it is highly unlikely that theValueTask
was not completed upon creation. So regarding your example the above advice has only theoretical value.ValueTask
s that are completed upon creation are not backed byIValueTaskSource<T>
instances, so there is no performance implication by not querying theResult
in your example. The advice would be of practical value if you queried theIsCompleted
property at a later time after creating theValueTask
.