4 ms·
>Prefer async/await over directly returning Task This one seems questionable to me. I've never been bitten by any of the cons mentioned[1], and it's even noted
by cmpb 5y ago
>Prefer async/await over directly returning Task
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/blob/master/AsyncGuidance.md#prefer-asyncawait-over-directly-returning-task https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/b...
- jayd16 5y agoThere's a couple in here that are the absolute safest things even though the alternatives can be done safely. Async void, for example, is for dealing with event handlers. 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.
- MaXtreeM 5y agoAlways using async/await is recommended to avoid _surprising_ behavior. If a method with a signature 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
- rawling 5y agoThis surprised me. When they wrote LINQ and iterables, MS went to great lengths to ensure exceptions that could be thrown immediately (before iterating) were. I wonder why async/await are the opposite.
- jkulubya 5y agoI don’t think I’d have a philosophical problem with throwing both synchronously and asynchronously when using async/await. After all, the act itself of queuing some work with a possible future result does seem like something that can fail. But the dotnet team (or c# compiler team, not sure) helped devs out by promising not to throw on the queuing the work bit when using async/await, and only throw at the point where the result should ordinarily be ready. 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?
- ziml77 5y agoIt's actually the case that's marked async that surprises me more. But I don't think the difference has ever mattered in code that I've written or worked with. 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.
- Salgat 5y agoUnless you're doing awaits in a tight loop of thousands/millions of calls, the overhead of the state machine is almost non-existent, which leads to the next question, what are you doing that requires await in a tight loop of that many calls? The whole point of await is to use it to yield a thread while waiting on a long running operation, if your await returns nearly instantly then use the synchronous version and avoid the overhead.
- zmj 5y agoI've run into this in code that completes synchronously in the common case, but falls back to an async implementation - think caching. The simple way to write this creates a state machine even on the synchronous path. 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.