locked
Async Syntactic Sugar Suggestions RRS feed

  • General discussion

  • I downloaded the Async CTP SP1 Refresh last night, refactored some code (An Open Source SugarSync API hosted at CodePlex), and I must say I am impressed!  Error handling and code readability have improved dramatically!  Getting up and running was easy after disabling ReSharper, looking at the examples, and taking some hints from Visual Studio.

    One thing I felt was a bit weird, was having to specify a return type of Task instead of void when the method doesn't return anything.  While I have accepted this, it feels weird.  Since we already have to decorate our method signatures with the async keyword, it seems it would be easier to have async void translate to async Task under the covers.

    // This...
    public async Task DoStuffAsync(){// no return here, what?}

    // Versus this...
    public async void DoStuffAsync(){// no return found here, feels natural}

    The same goes for the unwrapping of Task<T> when used in conjunction with the await keyword:

    // This...
    public async Task<int> GetANumber(){return 10;}

    // Gets called like this?
    int x = await GetANumber();

    I understand that the await keyword allows visibility into what calls are async, but can't we do this another way - like coloring the call, putting some smart tag / tooltip, etc?  I'd like to be able to write:

    int x = GetANumber();

    or

    DoStuff();

    Also, having to include the async keyword in the method signature to enable await usage seems odd.  This is another thing that could be automated.

    If, for some reason, you needed to get at the Task or Task<T> return values, would a cast make sense?

    Task<int> x = (Task<int>)GetANumber();

    Overall, a much needed addition to our already awesome toolset!  Thanks for the great work!

    Monday, July 18, 2011 10:31 PM

All replies

  • Welcome to the world of async!

    Async void is slightly different than async Task. An async Task can be composed into other async methods by using await. An async void, however, acts as a "top-level" asynchronous operation. On WinForms and WPF, async void methods can be directly used as event handlers. The disctinction between async void and async Task is even more important for ASP.NET asynchronous pages, for reasons described in my MSDN article. In summary, ASP.NET needs to keep a count of outstanding asynchronous operations so it knows when the page is complete; the async void methods increment that count when they start and decrement it when they finish.

    Regarding inferring the Task wrapper (e.g., declaring "async int GetANumber()" and having it transformed into "async Task<int> GetANumber()"): this was discussed in this thread. In summary, the consensus is that it's clearer to have the return type stay the same through the compiler transformation. This is especially true when you have one TAP method returning (not awaiting) a Task from another TAP method. Inferring the Task wrapper would also remove the distinction between async void and async Task, as described above.

    Regarding inferring the async keyword (based on the presence of an await within the method body): this was discussed in this thread (and also quite a bit in blog comments and Channel9). Eric Lippert has an excellent post on the subject. In summary, Microsoft will not introduce a breaking change to the C# language, and a single-word "await" keyword would be a breaking change. They had to choose between a multi-word await (e.g., "wait for"), or a keyword on the method (async) that would enable the "await" keyword. There's an additional code readability benefit to the async keyword as well: you can immediately see at the beginning of the method that it is an async method, rather than having to infer it yourself.

    Regarding inferring the awaits themselves (e.g., "DoStuff()" instead of "await DoStuff()"): this was briefly discussed in this thread. It's fine if you only consider the most basic scenario: where an async method has one "await" followed by another "await", all the way through the method. More complex async scenarios require compound operators such as "WhenAll" and "WhenAny", which allow starting several Tasks and then awaiting.

    As you suggested, you could allow the complex scenarios by casting to a Task, but then it seems strange to have an identity cast (to the same type) actually have a significant difference in semantics. There's also the question of how casts to other types (e.g., MyOwnCustomTask or uint or object) are handled.

    It would be possible to replace the await keyword with an opposite-of-await keyword ("nowait"?) and reverse the await semantics (i.e., always do implicit awaits unless the nowait keyword is present). I'd be interested in hearing an Async team member chime in on whether that approach was considered. At first glance, it seems to allow the same flexibility, but simplify the simple case (single awaits).

    OTOH, perhaps it would simplify it too much. The current system (await) allows the code reader to see exactly where the control flow passes back to the caller, so blocks of synchronous code are easily identified. Under the proposed system (nowait), the code reader must know the semantics of each method called in order to identify blocks of synchronous code. Furthermore, it seems that the nowait system would be more prone to breaking if a method is changed from blocking to async; under the await system, all callers of that method would need to be changed to include an await; under the nowait system, callers of that method would still compile but with subtlely different semantics.

    Hmmm... OK, I think the await system is better than a nowait system after all.

           -Steve


    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
      and How to Implement IDisposable and Finalizers: 3 Easy Rules
    Microsoft Certified Professional Developer

    How to get to Heaven according to the Bible

    • Edited by Stephen ClearyMVP Tuesday, July 19, 2011 2:56 PM Added more rambling thoughts to the end
    Tuesday, July 19, 2011 2:30 PM
  • After going through a few scenarios, the light bulb came on.  Sometimes we have to walk a mile in a man's shoes :-).

    Thanks for this addition to my arsenal of tools - it is going to drastically increase my efficiency, given the asynchronous nature of programming these days.

    Tuesday, July 19, 2011 10:09 PM