I want to call the following code:
public async ValueTask UserGivenClaim(AppUser user, Organization org, string claim)
{
var notify = new NotifyClaims(user, org);
await notify.SetReplyToTask(null, org);
await NotificationQueue.PushValueTask(notify);
}
In the code above SetReplyToTask()
returns a Task and PushValueTask()
returns a ValueTask. And from a performance point of view SetReplyToTask()
99% of the time does an if check that's true and returns. So 99% of the time it is not calling Task methods inside of it.
Declaring UserGivenClaim
async lets me call await notify.SetReplyToTask(null, org);
Will this be the fastest code? I worry that I've thrown away the advantage of ValueTask doing this.
The other possibility is to make notify.SetReplyToTask()
a ValueTask. If 99% of the time it is not calling a Task, is this when I should be using ValueTask?
The await
s in a ValueTask
returning async
method don't all have to be the same awaitable type, i.e. only ValueTask
or only Task
. Your example as is fine.
One of the main points of using a ValueTask
is when the async method will complete synchronously as you acknowledged:
If 99% of the time it is not calling a Task, is this when I should be using ValueTask?
So if
PushValueTask()
as ValueTask
returning we are sure that it will complete synchronously the majority of the timeSetReplyToTask()
although Task
returning, will also complete synchronously 99% of the time per your words.will make UserGivenClaim
itself complete synchronously the majority of the time (unless there are some interesting hidden dependencies between the two method calls inside of it).
That said, the documentation lists two other "requirements" that should be fulfilled before you abandon the default Task
:
A method may return an instance of this value type when it's likely that the result of its operation will be available synchronously, and when it's expected to be invoked so frequently that the cost of allocating a new Task for each call will be prohibitive.
You haven't mentioned anything about those two (frequent invocation, allocation costs) for your use case, so I think you might be unnecessarily overoptimizing by thinking about using a ValueTask
instead of a Task
.
In the docs you can see a lot of limitations that ValueTask
has that Task
doesn't, which make the choice between the two a bit more nuanced than "Is it completing synchronously the majority of the time".