I'm using a library that has both a synchronous and an async
version of a Reconcile
method.
The methods take 2 IEnumerables
, and 3 delegates that get called for items that are Added, Modified, or Deleted from the 2nd list based on values from the first.
My code is currently working with the synchronous version, and I'd like to convert it to using the async
version.
Since I don't actually need to do any work in the delegate for deleting, I'm passing in (item) => {}
for the deletedAction
argument.
I've found several different versions of how to convert that in to an "empty" async delegate scattered around the internet, and across StackOverflow, but I'm not sure of the difference between them, or which way is the most correct.
What are the differences between these methods of sending an "empty" async delegate as an argument, and which of them is the current "most correct" way? Is there a better way that I have missed?
async (item) => {await Task.CompletedTask;}
async (item) => {await Task.FromResult(0);}
async (item) => {await Task.Yield;}
async (item) => {await Task.Delay(0);}
(this one seems like a bad choice, but I'm including it for completeness)They all seem to be working, except for Task.CompletedTask
, but that is because the framework I'm using is the 4.5 version of the .Net Framework, and it doesn't exist in that version.
Async code can be used for both I/O-bound and CPU-bound code, but differently for each scenario. Async code uses Task<T> and Task , which are constructs used to model work being done in the background. The async keyword turns a method into an async method, which allows you to use the await keyword in its body.
An async method runs synchronously until it reaches its first await expression, at which point the method is suspended until the awaited task is complete. In the meantime, control returns to the caller of the method, as the example in the next section shows.
Task represents the execution of the asynchronous method, so for an asynchronous method to return a null task is like telling the calling code "you didn't really just call this method" when of course it did. So, a Task / Task<T> returned from a method should never, ever be null .
So none of them are correct. What you should be doing is:
item => Task.CompletedTask
Or, when on an older version of the framework:
item => Task.FromResult(0)
You have no reason to make the method async
just to await an already completed task. It's just adding overhead of the state machine in order to accomplish nothing.
Using Delay
is just going through an extra layer of indirection before returning a completed task. It's adding nothing useful except obscuring the fact that you're trying to return a completed Task
. It's also relying on an undocumented implementation detail that Delay
returns a completed task when the timeout is 0
, which is something to be avoided where possible.
Using Yield
is by far the worst. The whole point of Yield
is that it won't be observed as being completed right away. The goal of Yield
is to result in the continuation being added and fired, rather than the task being observed as being completed immediately. It exists specifically to avoid the optimization that you want to take advantage of.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With