-1

I'm new to C# asynchronous programming, sorry if my question sounds dumb. Below is some code:

static async Task Main(string[] args) {
   var result = TestMethod(false);   //result is still Task<string> rather than string
}

public static async Task<string> TestMethod(bool check) {
   if (check) {
      await Task.Delay(8000);
      return "Hello World";
   } else {
      return "Goodbye World";   // no await in else statement
   }
}

So there is no await in TestMethod's else statement, how does the code still compile? from my understanding, each possibile path should contain a await statement?

And result is still Task<string> rather than string, but the executation path only reach else statment which returns a string directly, how come result is still a task?

amjad
  • 3,048
  • 1
  • 11
  • 42
  • 5
    There is no requirement that the method have any `await` statements at all! (If you have none, you get a compiler warning, but the code will still compile.) The `async` keyword allows your method to use `await` but does not **require** it to use `await`. – Raymond Chen Nov 18 '20 at 22:44
  • _from my understanding, each possibile path should contain a await statement_ ...no, this isn't true at all. – ADyson Nov 18 '20 at 22:52
  • This is most definitely a duplicate, cant find one though – TheGeneral Nov 18 '20 at 22:52
  • A simplified explanation is that `Task` represents something that will happen in the background (on a different thread, or in the kernel, etc), and will provide a signal when it is done. `await` instructs the program to wait for that task to complete, before continuing. If you don't `await` it, the task will still be executed, at the same time as the calling method. You can end up with two methods running in parallel. Sometimes people do this intentionally, to get parallelism – Andrew Williamson Nov 18 '20 at 22:52
  • _returns a string directly, how come result is still a task_ ...this is the syntactic magic of the async keyword. Just a convenience basically to make the code easier to write and easier to read. – ADyson Nov 18 '20 at 22:53
  • @AndrewWilliamson I always thought the exectuation reach await, it returns a Task to the calling method, but the else statement doesn't contain await, how it still return a Task to the calling method? – amjad Nov 18 '20 at 23:01
  • @amjad I'm not sure Andrew's comment is really relevant to your question actually. It talks about the effects of not awaiting a method call which is asynchronous. But you're talking about not doing anything asynchronous at all - two different things. Raymond Chen's comment explains it much better. It still returns a Task because that's what the method signature says - and because `async` makes that happen automatically. Whether you do anything within the method which is _actually, genuinely asynchronous_ doesn't matter. – ADyson Nov 18 '20 at 23:02
  • 1
    _the exectuation reach await, it returns a Task to the calling method_ ...no that's not how it works at all. `await` effectively tells the current method to pause, and releases the thread it's running on, while the method being awaited is executed on another thread. If you have an async method without any awaits, then all that happens is that method runs completely within the same thread. – ADyson Nov 18 '20 at 23:04
  • @ADyson The quote you say is wrong is very much correct. You just said the same thing using, different words (and personally I prefer their phrasing to yours). The mechanism by which an async method "tells the current method to pause and relases the thread it's running on" is by returning an incomplete `Task` to the caller and adding a continuation to the awaited `Task` to resume the async method. – Servy Nov 18 '20 at 23:37
  • @amjad "how it still return a Task to the calling method?" It will do so when it reaches the return value, or when it throws an exception. If you mean how does it create the `Task` to return, it does that exactly the same way it does when you `await` it. – Servy Nov 18 '20 at 23:38
  • @Servy yes but OP I think meant that the Task was returned to the caller of _their_ async method (i.e. in the example they seemed to believe that the Task returned by `await Task.Delay(8000)` is then returned directly as the output of the `TestMethod` method (or somehow as the `Task` part of that output, if not the string). Clearly that doesn't make sense, but hence, I suspect, their confusion about how `TestMethod` still returned a Task even when the code path didn't include an `await`. That's how I interpreted that remark anyway. – ADyson Nov 18 '20 at 23:40
  • @ADyson That's certainly not what their quote says. Their quote says *a* task, not *the awaited* task. If you think that their interpretation was wrong then say that you think they may be mis-interpreting that quote, rather than saying that the quote is wrong when *the quote isn't wrong* and your clarification of it is, if anything, less correct. – Servy Nov 18 '20 at 23:43
  • @Servy read it in context: `the exectuation reach await, it returns a Task to the calling method, but the else statement doesn't contain await, how it still return a Task to the calling method` and maybe you'll see what I'm getting at. "calling method" there seems to refer to the method calling `TestMethod`. Doesn't make sense to me any other way when talking about the `else` since there's no other method call in that block...and presumably "calling method" refers to the same method when used each time in the sentence. But maybe I mis-interpreted it. Perhaps the OP could clarify. – ADyson Nov 18 '20 at 23:44
  • @ADyson I did read it. It still doesn't say what you're saying that it does. Again, if you think they may be misunderstanding a correct statement then by all means say that you think they may be misunderstanding it, but don't say that the statement is wrong *because it isn't*, and if they didn't misinterpret it in the way you thought they were you've now caused a very confusing situation. – Servy Nov 18 '20 at 23:47
  • @ADyson You say it doesn't make sense any other way, but the statement is 100% correct, and is the way it actually works. When the method reaches its first `await` *it returns a task to the calling method*. Now it's a newly created `Task`, not the one that was awaited, but the quote says, "a task" not "the task" or "the awaited task" or anything else to indicate it's not a new task. So again, if you think they didn't understand that nuance, by all means, elaborate on it and be more explicit, but saying that it's wrong *when it is not at all wrong* is problematic. – Servy Nov 18 '20 at 23:50
  • @Servy ok so maybe I should have quoted the whole thing rather than just that bit, and explained that it seemed ambiguous. Point taken. – ADyson Nov 18 '20 at 23:52
  • @Servy Thank for you answer. So you mean even though there is no `await ` in else statement, the compiler sees the `async` keyword and it will still treat `return "Goodbye World";` as `return Task;`, is my understanding correct? – amjad Nov 19 '20 at 00:45
  • 2
    @amjad The compiler radically changes all `async` methods into much more complicated methods that do a lot of things (regardless of whether or not you're taking advantage of all of the work it's doing). So it's not *that* simple, but it will return a new task in *some* way in all situations. – Servy Nov 19 '20 at 00:59

1 Answers1

1

The async keyword in the method definition signals to the compiler that it should return a task whose result will be whatever is returned by the return statement of the method. If you remove the async keyword, the compiler will complain about returning a string instead of a Task<string> like you were expecting.

You can read the Microsoft Docs about async return types here.

This doesn't actually require any await statements within the method but if you remove the await Task.Delay(8000); line, you will likely see a warning saying that the method will run synchronously (assuming that you are using Visual Studio).

Connell.O'Donnell
  • 3,356
  • 11
  • 25
  • 51