Just like returning Task directly, if you take care with the exceptions, you'll have less problems.
Said another way "...unless you know what you're doing" could be added to a few of these.
Task<Bar> Foo();
and it is not declared with async and it throws an exception, the exception is propagated directly to the call site. Think of this usage:
var getBarTask = Foo();
// do some other stuff
try { var bar = await getBarTask; }
catch (Exception ex) { handle exceptions }
Then if the Foo is not async the exception is thrown at 'var getBarTask = Foo();'. If it is declared with async the exception is wrapped inside of the Task object and thrown at the 'var bar = await getBarTask;'
Yes there is obviously a small performance cost. My guideline would be "always use async/await unless you call the method hunders or more times a second and the small performace cost becomes neglible. And always measure before you optimise.
edit: formating
The reason that the non-async case makes sense to me is that I know there's usually going to be some synchronous code execution before the function I'm calling has to go async. And in that case I expect the code the executed before going async to come up the stack where I called it instead of where I'm awaiting it. And of course I expect exceptions beyond that to only be able to be retrieved when I await the task since the call stack will be rooted in the event loop after going async.
If you don’t use async/await, then I’m not sure how else they can help. By returning a task without async, the dev claims that they’re smart enough to safely kick off some async work and possibly provide a result later. But in the act of kicking off the work, you break?
It's possible to work around this efficiently by pulling the async code into a separate method and using ValueTask for the outer method return type.
This one seems questionable to me. I've never been bitten by any of the cons mentioned[1], and it's even noted that doing it this way does incur performance costs. I've learned over the years that if the code path is very prolific, it pays to avoid the async state machine.
I'm curious if others could expand on this one.
[1] https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/b...