已锁定 Async / Await syntax alternative

  • 9. dubna 2012 21:31
     
      Obsahuje kód

    I know that a lot has been said on the subject of alternative syntax for await and async keywords. But since I was too busy when it happened, I didn't have the chance to give my suggestion. As someone may find it interesting, I am posting it now.

    My idea is not so much to find an alternative to the keywords, but rather a comment on the very structure of the syntax. What I suggest is the following:

    1. Mark code blocks rather than methods to be async.
    2. Declare the block async in the same form as "using" or "unsafe"
    3. Merge async and await in a single after keyword (or when?).

    So in the end the code would look like this:

    public void StartDayAtTheZoo()
    {
       after(var lionFood = GetLionFoodAsync())
       {
          FeedLion(lionFood);
       }
    
       after(var okapiFood = GetOkapiFoodAsync())
       {
          FeedOkapi(okapiFood);
       }
    }


    Instead of:

    public void StartDayAtTheZoo()
    {
       StartDayAtTheLionCage();
       StartDayAtTheOkapiCage();
    }
    
    public async void StartDayAtTheLionCage()
    {
       var lionFood = await GetLionFoodAsync();
       FeedLion(lionFood);
    }
    
    public async void StartDayAtTheOkapiCage()
    {
       var okapiFood = await GetOkapiFoodAsync();
       FeedOkapi(okapiFood);
    }

    I think that overall it is more inline with what happens under the hood, and as such, much less confusing to the beginner.

    I would also argue that the use case of chaining asynchronous tasks is much less frequent than "parallelizing" them. And so the syntax that "parallelize" them should be preferred. I believe the syntax I am suggesting is better in that sense.

    In fact, I would also like to suggest that as in Linq, there should be the highlevel-magic-like syntax (from x in ...) and a delegate-friendly syntax ( list.Select( x=> ... ) ) for asynchronous actions. And I am pretty sure I will use the following code in all my projects using async;

    internal class TaskContinuator<T> 
    {
       private Task<T> _task;
       private Action<T> _action;
    
       public TaskContinuator(Task<T> task, Action<T> action)
       {
          _task = task;
          _action = action;
       }
    
       public async void AsynchronousChain()
       {
          T data = await _task;
          _action(data);
       }
    }
    
    public static TaskExtensions
    {
       public static void AfterWhat<T>(this Task<T> task, Action<T> action)
       {
          var continuator = new TaskContinuator(task, action);
          continuator.AsynchronousChain();
       }
    }

    This then allows me to do:

    public void StartDayAtTheZoo()
    {
       GetLionFoodAsync().AfterWhat( lionFood => { FeedLion(lionFood); } );
       GetOkapiFoodAsync().AfterWhat( okapiFood => { FeedOkapi(okapiFood); } );
    }

    Now this may look more complicated to many people, but remember how developers embraced the extension-based linq syntax, as it was much closer to what was happening behind the scene.

    Another benefit of that syntax is that it is not "overselling" the capacity of the new async. The new async is great, that's a fact, but it is hard to understand for the beginner that it is limited to asynchronous actions (or async void methods) and is not a generic asynchronous method thing. In other words, when going into the async world, there is no way back. It is saying "once you finished your async method, do this and die" and not "once you are finished with your multiple async methods, get back to work with your various results". You can obviously make the latter happen with a little work by saying "once you finished that async method, store the results in some variable, check if everything else is finished and if yes, get back to work with all the results". Realizing this is the most important thing to get when working with async.

    Now, I am certainly not a master in language design, and I surely don't know what's possible or not. So I would be happy to hear any comment from people who know better.

















    • Upravený d--b 9. dubna 2012 21:41
    • Upravený d--b 9. dubna 2012 21:42
    • Upravený d--b 9. dubna 2012 21:44
    • Upravený d--b 9. dubna 2012 21:47
    • Upravený d--b 9. dubna 2012 22:04
    • Upravený d--b 9. dubna 2012 22:05
    • Upravený d--b 9. dubna 2012 22:06
    • Upravený d--b 9. dubna 2012 22:14
    • Upravený d--b 9. dubna 2012 22:18
    • Upravený d--b 9. dubna 2012 23:34
    • Upravený d--b 9. dubna 2012 23:37
    • Upravený d--b 10. dubna 2012 12:18
    • Upravený d--b 10. dubna 2012 12:33
    • Upravený d--b 10. dubna 2012 14:58
    •  

Všechny reakce

  • 12. dubna 2012 12:38
     
      Obsahuje kód

    If you often write code like this, then I think you don't need any special syntax, you can simply use ContinueWith():

    public void StartDayAtTheZoo()
    {
       GetLionFoodAsync().ContinueWith(t => FeedLion(t.Result));
    
       GetOkapiFoodAsync().ContinueWith(t => FeedOkapi(t.Result));
    }

    So, your syntax kind of solves already solved problem.

    But your syntax is also much less flexible than await. How would you write code like the following?

    byte[] buffer = new byte[4096];
    while (true)
    {
        int read = await inputStream.ReadAsync(buffer, 0, buffer.Length);
        if (read == 0)
            break;
    
        await outputStream.WriteAsnyc(buffer, 0, read);
    }
    • Upravený svick 12. dubna 2012 12:39
    •  
  • 13. dubna 2012 12:32
     
     

    Oh nice one, I didn't know ContinueWith() and I see your point on the second comment. I guess what I had not understood is that this is really not a mechanism to handle parallel asynchronous methods running, but something that gives back the hand to an event loop (or whatever the calling function is) while it's doing costly operations.

    In other terms, it's like saying to the UI, "this operation will take some time so please do your events meanwhile". There is little if no other use of async/await than talking back to the UI, is there? 

  • 13. dubna 2012 15:53
     
     

    There is one other major reason for using async: efficiency. Threads are a relatively expensive resource, so you should use them efficiently. This applies especially in an environment that already uses threads a lot, like ASP.NET.

    If you make a synchronous IO request, you're blocking a thread while the operation completes. But that's wasteful, that thread could be doing some useful work in the meantime, like processing another request.