What the async and await keywords apply to? [closed] - c#

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 months ago.
Improve this question
I'm completely confused.
It says here:
Async methods can have the following return types:
Task, for an async method that performs an operation but returns no
value.
Task <TResult>, for an async method that returns a value.
void, for an event handler.
Starting with C# 7.0, any type that has an accessible GetAwaiter
method. The object returned by the GetAwaiter method must implement
the System.Runtime.CompilerServices.ICriticalNotifyCompletion
interface.
Starting with C# 8.0, IAsyncEnumerable<T>, for an async method that
returns an async stream.
Question 1. Why write about tasks if Task and Task<Result> fit the "GetAwaiter" criterion?
Question 2. Here I got the answer that the IAsyncEnumerable<T> point is more about await. Then what about everything else? Can someone clearly explain what each keyword applies to without big examples?
What types can return async methods - list or criteria? And what types can the await keyword or their criteria apply to?
Examples of answers how I would like:
Keyword async applicable for return values (async methods can return values): type1, type2, typeN, or any type with specific characteristics (for example ... some characteristics).
Keyword await applicable for awaitable types: type1, type2, typeN, or type with specific characteristics (for example contains GetAwaiter).
Question 3. Speaking of void we mean only the return value of the async method. Can void be the awaitable type? Can someone give an example of a void awaiting?

Why write about tasks if Task and Task fit the "GetAwaiter" criterion?
Probably because Task/Task<Result> will be used in 90% or so of all cases. It hints to a new developer to just use these types, and not try to implement a custom type for this, or spending extra time looking up the documentation.
Question 2. Here I got the answer that the IAsyncEnumerable point is more about await. Then what about everything else? Can someone clearly explain what each keyword applies to without ten-story examples?
async is used to tell the compiler to rewrite the method into a state machine, so that different parts of the method can be run at different times. If the method returns anything, this needs to be wrapped in something that allow the caller to use the result when it is actually produced.
await is used in an async method suspend execution of the method until the awaited value is available. I.e. it marks where the compiler will split the method into the different sections.
I think you are making this more complicated then it needs to be:
Use Task when you do not need to return a value
Use Task<Result> when you return a value
Use IAsyncEnumerable<T> when producing a sequence of values asynchronously. I.e. each value is produced by a separate network/Database call. Contrast with Task<T[]> when one IO call produces a sequence of values.
Use ValueTask when you want to minimize overhead. See Understanding the Whys, Whats, and Whens of ValueTask
Use void when you are forced to. I.e. UI event handlers.
Use a custom type when you have some special reason to. This should be quite rare, I would expect 99% of developers never to write any such implementation.
Speaking of void we mean only the return value of the async method, is void not the expected type?
A normal non-async void method can actually produce two results:
Nothing
An exception
And the result is only produced once the method has reached its end. Awaiting a method returning a Task replicates this behavior. An async void method however does not. Since there is nothing to await, the caller have no way of knowing when, and if the method reaches its end. And exceptions may be lost, without anyone knowing it occurred. This is acceptable for things like UI events, after all, why does the UI system does not really care about what happens when a button is pressed, it is up to the developer to do things like handling exceptions. But in most other cases, Task should be used to represent a synchronous void-method.

Related

Why isn't WebRequest.GetResponseAsync() async?

As far as I know WebRequest.GetResponseAsync() can be awaited and is asynchonous.
But when I look at its source code at https://referencesource.microsoft.com/#System/net/System/Net/WebRequest.cs,99a2a9c8ebc067dc
it isn't defined with the "async" keyword.
I thought you cannot await a method that is not defined as "async"?
I thought you cannot await a method that is not defined as "async"?
This is a kind of "shorthand" way of thinking about it that can be useful while initially learning async, but falls apart when you gain more knowledge about async/await. There are two concepts that are not quite right in this sentence.
First, code cannot await a method. What actually happens is that the method is called, exactly like any other method. That method returns a value - an awaitable type - and it is that return value that is actually awaited. It's common for us to talk about "awaiting a method", but that's just shorthand for "calling a method and awaiting its return value", which is more accurate but too awkward for most conversation.
Second, async is just one way to create an awaitable return value (which can then be awaited). There are several other ways as well. For example, Task<T>.Factory.FromAsync is commonly used if you have an API that uses the older APM asynchronous pattern, and you want to use it with await. In that case, you would normally write wrapper methods that return Task<T> and are implemented using Task<T>.Factory.FromAsync (without async). Another common approach is to use TaskCompletionSource<T>, which can create a Task<T> around any kind of logical "future event".
To put it another way, async is an implementation detail. This concept is further enforced by the fact that interface methods can be declared as returning Task and awaited, but only method implementations can be declared with async.

Does adding a Task<T> to the return of a method make it run in a thread?

I am struggling to find any solid information online, but lets pretend I have a method like below:
public int DoSomething()
{
// some sync logic that takes a while
return someResult
}
So lets assume it is a method that is completely sync and currently will block the thread the caller is on for 100ms. So then lets say I wanted to create a version that would not block that thread while it executes, is it as simple as just doing:
public Task<int> DoSomethingAsync()
{
// some sync logic that takes a while
return Task.FromResult(someResult)
}
As I know Task.Run will cause code to be executed within a thread, but does .net basically interpret any method with a Task/<T> return type to be something that should be non blocking?
(I am hesitant to use the word async as it has mixed meanings in .net, but as shown above there is no await use here)
No. All that changing the return type does is... change the return type.
You're changing your promise from "when I return I will give you an int" to "when I return, I will give you something that will eventually provide an int" (with the caveat that the value may, in fact, already be available).
How your method comes up with the Task is entirely an internal implementation detail that is a concern for your method. In your current changed implementation, it does it by running entirely on the same thread from which it was called and then just stuffing the result into a Task at the end - to no real benefit.
If you had added the async keyword and left the return someResult; line you'd have been given more clues about this by the compiler:
This async method lacks 'await' operators and will run synchronously. Consider using ...

Is returning a Task violating the CQS-principle?

The CQS-principle (https://en.wikipedia.org/wiki/Command%E2%80%93query_separation) states that a command should return void.
The recommendation for async methods is to never return void (https://msdn.microsoft.com/en-us/magazine/jj991977.aspx), but instead returning a Task.
So, if I write an async command, will that inevitably break the CQS-principle?
When dealing with async Task represents void and Task<T> represents "a result". So no, it does not violate CQS you just need to think of Task as void.
At the level of considering what you want to know (a query) or do (a command) then Task<T> gives you data and hence is correct for a query and Task does not and hence is correct for a command. ("return void" is the language-specific way for some langauges to express "do not return data").
At the level below that, in which you are considering the mechanism by which asynchronous operations are managed, then you always want to have information about the state of the asynchronous operation and so always want some sort of task object. That is not the level to consider command-query separation.
Comparably, if a .NET method called into a COM method it would be calling into code that always returned a value indicating success or failure. That is just an implementation detail of how exception-handling happens in that particular technology. It's either vital or irrelevant to think about this depending on the level you are working at. Task is an implementation detail of how task-based asynchronous programming works. It's either vital or irrelevant to think about this depending on the level you are working at.
The level at which you need to think about "is this a command or a query", Task is an implementation-detail about how you get void.
From the article you are linking:
When converting from synchronous to asynchronous code, any method
returning a type T becomes an async method returning Task<T>, and any
method returning void becomes an async method returning Task.
Therefore returning Task (not Task<T>) translates to having a method returning void.

Why should a void method be async to be able to await?

Assume I have a method that has void return type and that I need to await on an operation in this method.
public void someOperation()
{
//dostuff
var res = await someOtherOperation();
//do some other stuff that needs res.
}
When I tried to compile this code I got the error saying someOperation has to be declare async. I don't understand why. I understand why it would if the method had a return value, but not here when it's void. or even in the case when the awaiting operation has no effect on the return value of method.
This has already been asked as a part of this question but I didn't find the answer I was looking for. This guy merely mentions:
The async keyword enables the await keyword. So any method using await
must be marked async.
First of all, I'm not sure what it means that async keyword enables await keyword.
And secondly, restating my question, I'm not sure why it is required?
Asynchronous methods must be marked async. The async and await keywords are paired together, primarily for backwards-compatibility reasons; await was a valid identifier (not a keyword), so when it was added to the language, they also added the async keyword.
Alternatively, they could have gone with a multi-word keyword for await, such as await for. However, the language design team decided to go with the pairing, since it clearly and explicitly marks all async methods - it's easier for both compilers and humans to parse.
I have a blog post on the subject, with links to Eric Lippert's definitive blog post as well as discussions in blog comments, Channel9 forums, MSDN forums, and of course right here on Stack Overflow.
Note that the natural return type of an async method without a result value is Task, not void. void is an unnatural return type for async methods. So your default reaction when seeing the "await operator must be within an async method" error should be to mark it async and change the return type from void to Task:
public async Task someOperationAsync()
{
//dostuff
var res = await someOtherOperationAsync();
//do some other stuff that needs res.
}
This follows one of the best practices in my MSDN article on the subject: avoid async void. async void was allowed by the language designers for event handlers (or code that is logically an event handler, like ICommand.Execute); any other usage is going to cause problems.
I don't understand why.
Because you need to tell the compiler that you're trying to write an async method. That's exactly what the document you're quoting means.
The compiler could infer that from whether or not your method includes any await expressions - but that would mean you could comment out one line of a method and the whole method's behaviour would radically change. (Think about exception handling, for example...)
It also improves readability - it means anyone reading your code knows this is an async method right from the start.
So basically, the answers are:
You need to because the language specification requires you to. The compiler is implementing the language specification.
The language specification requires you to because it reduces the element of surprise.
Because they must work in pair in C#. It is the rule.
The “async” keyword enables the “await” keyword in that method and changes how method results are handled. That’s all the async keyword does! It does not run this method on a thread pool thread, or do any other kind of magic. The async keyword only enables the await keyword (and manages the method results).

What's the difference between returning void and returning a Task?

In looking at various C# Async CTP samples I see some async functions that return void, and others that return the non-generic Task. I can see why returning a Task<MyType> is useful to return data to the caller when the async operation completes, but the functions that I've seen that have a return type of Task never return any data. Why not return void?
SLaks and Killercam's answers are good; I thought I'd just add a bit more context.
Your first question is essentially about what methods can be marked async.
A method marked as async can return void, Task or Task<T>. What are the differences between them?
A Task<T> returning async method can be awaited, and when the task completes it will proffer up a T.
A Task returning async method can be awaited, and when the task completes, the continuation of the task is scheduled to run.
A void returning async method cannot be awaited; it is a "fire and forget" method. It does work asynchronously, and you have no way of telling when it is done. This is more than a little bit weird; as SLaks says, normally you would only do that when making an asynchronous event handler. The event fires, the handler executes; no one is going to "await" the task returned by the event handler because event handlers do not return tasks, and even if they did, what code would use the Task for something? It's usually not user code that transfers control to the handler in the first place.
Your second question, in a comment, is essentially about what can be awaited:
What kinds of methods can be awaited? Can a void-returning method be awaited?
No, a void-returning method cannot be awaited. The compiler translates await M() into a call to M().GetAwaiter(), where GetAwaiter might be an instance method or an extension method. The value awaited has to be one for which you can get an awaiter; clearly a void-returning method does not produce a value from which you can get an awaiter.
Task-returning methods can produce awaitable values. We anticipate that third parties will want to create their own implementations of Task-like objects that can be awaited, and you will be able to await them. However, you will not be allowed to declare async methods that return anything but void, Task or Task<T>.
(UPDATE: My last sentence there may be falsified by a future version of C#; there is a proposal to allow return types other than task types for async methods.)
(UPDATE: The feature mentioned above made it in to C# 7.)
In case the caller wants to wait on the task or add a continuation.
In fact, the only reason to return void is if you cannot return Task because you're writing an event handler.
Methods returning Task and Task<T> are composable - meaning that you can await them inside of an async method.
async methods returning void are not composable, but they do have two other important properties:
They can be used as event handlers.
They represent a "top-level" asynchronous operation.
The second point is important when you're dealing with a context that maintains a count of outstanding asynchronous operations.
The ASP.NET context is one such context; if you use async Task methods without awaiting them from an async void method, then the ASP.NET request will be completed too early.
Another context is the AsyncContext I wrote for unit testing (available here) - the AsyncContext.Run method tracks the outstanding operation count and returns when it's zero.
Type Task<T> is the workhorse type of the Task Parallel Library (TPL), it represents the concept of "some work/job that is going to produce a result of type T in the future". The concept of "work that will complete in the future but returns no result" is represented by the non-generic Task type.
Precisely how the result of type T is going to be produced is and implementation detail of a particular task; the work might be farmed out to another process on the local machine, to another thread etc. TPL tasks are typically farmed out to worker threads from a thread pool in the the current process, but that implementation detail is not fundamental to the Task<T> type; rather a Task<T> can represent any high-latency operation that produces a T.
Based on your comment above:
The await expression means "evaluate this expression to obtain an object representing work that will in future produce a result. Sign up the remainder of the current method as the call back associated with the continuation of that task. Once that task is produced and the call back is signed up, immediately return control to my caller". This is opposed/in contrast to a regular method call, which means "remember what you're doing, run this method until it is completely finished and then pick up where you left off, now knowing the result of the method".
Edit: I should cite Eric Lippert's article in October 2011 MSDN Magazine as this was a great help to me in understanding this stuff in the first place.
For loads more infromation and whitepages see here.
I hope this is of some help.

Categories