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?
CodePudding user response:
Good question. Yes, it is perfectly OK to omit querying the Result
property of a ValueTask<T>
, provided that you are not interested about the result, or about the Exception
that might be stored inside the task. It is also OK to ignore entirely a ValueTask
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 by await
ing them or by AsTask
ing them or by querying their Result
(provided that they are already completed). By consuming a ValueTask
you signal to the underlying implementation that the backing IValueTaskSource<T>
instance can be reused. Reusing IValueTaskSource<T>
instances is how the ValueTask
-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 the ValueTask
, so it is highly unlikely that the ValueTask
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 by IValueTaskSource<T>
instances, so there is no performance implication by not querying the Result
in your example. The advice would be of practical value if you queried the IsCompleted
property at a later time after creating the ValueTask
.