I suggest you ...

Don't require "async" keyword for the methods with "awaits" in the body

This is misleading. This looks like a part of method signature but this is not. The only thing it does is: it enables awaits in the method body. This requirement looks the same as if you would require "enable_yield" keyword for the methods with "yields":

public enable_yield IEnumerable<int> Foo()
{
yield return 42;//yield is enabled
}

public IEnumerable<int> Bar()
{
yield return 42;//compiler error
}

The "async" tells nothing new for the caller - the caller already knows that the method returns Task.
If the method returns "void" - anyways any methods can start threads, enqueue work items to the ThreadPoll etc.
Someone could think "the method is marked as async, so this is called in a separate thread", but that is wrong!

Don't require this keyword and even show the compiler warning: "This is obsolete".

63 votes
Vote
Sign in
(thinking…)
Sign in with: facebook google
Signed in as (Sign out)
You have left! (?) (thinking…)
Artem Elkin shared this idea  ·   ·  Flag idea as inappropriate…  ·  Admin →

13 comments

Sign in
(thinking…)
Sign in with: facebook google
Signed in as (Sign out)
Submitting...
  • Anonymous commented  ·   ·  Flag as inappropriate

    I would rather ask to not return a Task when method is marked as async, like having

    private Task<int> GetMyInt()
    {
    return Task.FromResult(1);
    }

    to be able to write

    public async int GetInt()
    {
    return await GetMyInt();
    }

    instead of

    public async Task<int> GetInt()
    {
    return await GetMyInt();
    }

    so in this case even the "async void" starts to make sense

  • Artem Elkin commented  ·   ·  Flag as inappropriate

    Imagine you have a project which you are migrating to the new .net with the new compiler.

    Situation 1: There are no classes/methords/fields/variables named "await" in the project (I believe this is true for more than 99.99% projects) => no problems with compilation. After migration you can add some methods returning Tasks (or other awaitables) which use the new "await" feature.

    Situation 2: You use "await" as a name of something. You have the following options:
    1. Specify the compiler option disabling the "await" feature. After migration you can use the new "await" feature but you should add [EnableAwait(true)] for the methods or classes (that enables this for all their methods).
    2. Add [EnableAwait(false)] where you use the "await" classes/methods/... => After migration you can add some methods returning Tasks (or other awaitables) which use the new "await" feature. To access the existing classes/methods/... named "await" you use '@' prefix: '@await'.
    3. Rename them. Goto "Situation 1". :)

  • Anonymous commented  ·   ·  Flag as inappropriate

    @Angelos You are fundamentally misunderstanding what async/await does. It does not imply multithreading. That's just one possible use case of many.

  • Angelos Petropoulos commented  ·   ·  Flag as inappropriate

    I don't like this design, I think it's a bad thing to add to the language and here is why:

    When I consume a method marked as "async" I have no guarantee what thread the method's code gets executed on, unless I read the method's body and understand the code for myself. This is exactly the same as before this keyword was introduced.

    So in order to use this keyword correctly I have to read and understand the code I'm calling. No benefit there.

    In order to use this keyword incorrectly and introduce deadlocking and other bugs in my code all I have to do is assume that the method I'm calling is going to run on another thread because it has the keyword "async". This is what anyone would expect unless told otherwise.

    In summary:
    - If I know the real behavior of the async keyword it offers me nothing over syntax sugar
    - If I don't know the real behavior and I just go with what seems obvious I can introduce very hard to diagnose bugs to my code, such as deadlocks

    Multithreading is hard. It's a good goal to try to make it easier to write multithreaded code, but don't go to extremes. This is an extreme: for very little benefit you are potentially leading developers to introduce very difficult to diagnose bugs in their code.

    No, I don't know how to design it better but that's not the purpose of uservoice.

  • lazydev commented  ·   ·  Flag as inappropriate

    I made this point few times when it was in development but in the end they found some justication for it.

    If it's going to stay (probable) one thing that might be nice is IDE auto-completion that detects if you are writing something that requires async, it would then add that. Not really big deal but I don't like this and the Try..(!...) pattern where you have to go edit a previous line in the midst of writing a new line.

  • Artem Elkin commented  ·   ·  Flag as inappropriate

    "return task;" could be a shortcut for "async return (async await task);" in that case. Anyways this is breaking change...

    Attribute is better because everybody knows that attribute is not a part of the method signature.

    And "the latest version" of my suggestion is:
    1. Don't require "async" + show warning.
    2. Compiler option to disable "await" keyword.
    3. [EnableAwait(true|false)] to enable or disable await for some method or class.

    Of course you can disable await by the compiler option but enable it for a certain method or class by the attribute. And you can access the classes, methods etc. with the name "await" l using @: "new @await(arg);". This is allowed even in the context where the await keyword is enabled.

  • Lucian Wischik commented  ·   ·  Flag as inappropriate

    @Artem your proposal to combine "async return" and "return" would be pretty weird because plain old "return" would be disallowed if it happened after an "async await" had already happened. That's not the same as coming lexically before, and is undecidable. So you'd end up with an oddly strict compiler rule that was strict enough (e.g. "you can't have async return in a method which also has async await in a conditional block like if, while, for that comes earlier; also you can't combine it with goto or try/catch or other forms of flow control"). Or you'd end up with runtime exceptions at times that happened.

    Also, the idea that an attribute [Async] is in any way better than a keyword "async"? I truly don't understand this.

  • Artem Elkin commented  ·   ·  Flag as inappropriate

    Defaults should be appropriate for the most cases (I haven't ever seen method, class, field or variable with the name "await").
    No async keyword in method headers, enable await by default!
    To "switch off" the await keyword simply add [EnableAwait(false)] to the method, class or assembly.

  • Artem Elkin commented  ·   ·  Flag as inappropriate

    From my point of view an ideal solution is:

    1. no "async" in the method header
    2. "async await" instead of "await"
    3. "async return" instead of just "return"

    With the last point you would have an additional feature: you could have "return" and "async return" in the same method:

    Task<string> FooAsync()
    {
    ..if(moon.Phase == FullMoon)
    ..{
    ....return DogeAsync();//so wow
    ..}

    ..async return "Call me later";
    }

    Task<string> DogeAsync()
    {...}

    Unfortunately I haven't finished implementing the time machine (I'm still awaiting for the AnswerUltimateQuestion() method results which are required to proceed) so I'm posting this into 2014 where (when) these "ideal" changes are already treated as broken ones.

    The [EnableAwait] attribute with the compilation warning on the async keyword look like a good trade off, I believe.

  • Artem Elkin commented  ·   ·  Flag as inappropriate

    [EnableAwait] is better name for the attribute. The attribute should be allowed to be specified for methods, classes and assemblies. A compiler option (switched on by default for VS projects) is also desired.

  • Artem Elkin commented  ·   ·  Flag as inappropriate

    Good explanation for why the async keyword is needed: http://blogs.msdn.com/b/ericlippert/archive/2010/11/11/whither-async.aspx
    My suggestion is "please achieve backward compatibility with common sense too". :)
    Possible solution is to explicitly specify the AsyncStateMachine attribute ( http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.asyncstatemachineattribute(v=vs.110).aspx ) or create and use AsyncAttribute:

    [Async]
    Task<int> AnswerUltimateQuestion()
    {
    await Task.Delay(7½ million years);
    return 42;
    }

    And it should be possible to specify attributes for the underlying methods of lambdas: [Async]()=>{...} (this is another feature request).

Feedback and Knowledge Base