Home > Uncategorized > C# 5.0 async/await and GUI events

C# 5.0 async/await and GUI events

A silly example. I’m tickled by the idea of treating a button as a simple task:

async void RefreshButtonAsync()
{
    for (;;)
    {
        await refreshButton.ClickAsync();
            // returns a Task that finishes 
            // next time the button is clicked
        await RefreshAsync();
    }
}

Wait for the button to be clicked, wait for the refresh to finish, repeat forever… that’s your task, little fella!

The nice thing about this is it automatically handles something you might overlook: if the user clicks refreshButton twice, you probably don’t want to start two overlapping refreshes. We’re either awaiting a click or we’re awaiting a refresh – never both at the same time. The imperative style of the code expresses this non-overlapping state machine perfectly.

It’s also a nice example of an infinite loop with a good purpose. If there’s never a click, the continuation never continues, and that’s fine. Responsive GUI applications don’t terminate like classical algorithms.

In the “startup” method for the dialog:

await RefreshAsync();
RefreshButtonAsync(); 

Even this simple example demonstrates (to me, anyway) how much nicer it would be if the compiler inferred what we meant, without us needing so many awaits – and the Async-suffix naming convention becomes a lot less valuable:

async void RefreshButton()
{
    for (;;)
    {
        refreshButton.WaitForClick();  
        Refresh();
    }
}

// elsewhere...
Refresh();
start RefreshButton(); 

It depends how heavily the feature is used. If it becomes second nature to people, then all those awaits are going to start looking like unnecessary noise. From the caller’s perspective, they mean very little, because the whole point of normal imperative programming syntax is that it implies waiting for results. The only difference here is the technicalities of how the waiting is accomplished.

About these ads
Categories: Uncategorized Tags: , ,
  1. configurator
    October 30, 2010 at 8:45 pm

    I think you’re missing the point of asynchrony here a bit – I think the standard way to do things would be to not ‘await’ on every call to the async methods, rather to get a task, and only await later. Consider developing a browser. Would you download and await for each file, or would you do something like
    async void Download(IEnumerable urls) {
    List<Task> tasks = new List<Task>();
    foreach (Url url in urls) {
    tasks.Add(DownloadDataAsync(url));
    }

    await Task.WhenAll(tasks);
    }

    Asynchrony lets you do that easily. The way you describe it, you’d have to pepper your code with ‘start’s and then have a weird Task.WhenAll(tasks)() at the end.
    Also, asynchrony lets you easily do stuff like “only download one files from a single server at the same time, but download from multiple servers simultaneously”, albeit with more logic in the handling class. It seems to me like the examples you’re using are simple and so it seems that you would usually wait immediately when calling a task, but in real life usage you’d normally await later.

    • earwicker
      October 30, 2010 at 11:53 pm

      I get the point of asynchrony. The reason I’m excited about this feature is because I’ve been working in the browser for a few months and using asynchrony all over the place, sometimes sequentially, sometimes in parallel, writing my own JS version of Task.WhenAll, etc, but having to do it all with closures and doing my own loop transformations… I’ve wanted this feature the whole time. I’m thinking ahead to when it is considered normal, rather than new and cool like it is now, and we’re back to the basic philosophy: what does imperative/sequential programming style mean? It means we wait. That’s implied automatically. Therefore it shouldn’t need to be stated. Rather, you should explicitly request not to wait. It should be that way round. That’s what this programming style means. It’s the divergences from that rule which should be indicated by a keyword.

      In your browser example, this line is the special case:

      tasks.Add(DownloadDataAsync(url));

      You’re calling a method to download some data, but rather than wanting to put the data on the list, you want obtain something representing the operation to download the data, and put that on the list. So that’s where I say the keyword should go, to indicate that you don’t want to treat the method call as you would if it wasn’t implemented asynchronously.

      tasks.Add(start DownloadDataAsync(url));

      As for the call to WhenAll, I think that should just be:

      Task.WhenAll(tasks);

      The () suffix (or some keyword alternative) would only be needed when the expression is not a method call returning a task-like object. It just looks a little less weird than:

      someTask;

      C# is doing a great job of bringing powerful features from various less widely-used languages and making them work in a widely-available commercial platform by compiler transformations: lambdas, iterators, and now async. Maybe the 5.0 CTP is the way it is because they already considered doing it transparently and hit a lot of unfixable issues. On the other hand, maybe they just picked the default that makes sense to the language implementors (put the keyword where the new implementation complexity is introduced) instead of what will make sense to a language user in ten years time when perhaps the majority of library APIs are returning Task.

      • configurator
        October 31, 2010 at 12:00 am

        In this case I couldn’t agree less with you. The compiler will insert special code unless you put the keyword in, but only in async methods. Don’t you see how fragile that is? I move a line of code from an async method to a non-async one, and it does something completely different? C# is built in a no-magic world. Nothing happens automatically unless you request it. Your suggestion is like having iterator blocks be defined like this:

        iterator IEnumerable Numbers(int start, int end) {
        for (int i = start; i <= end; i++) {
        return i;
        }
        }

        Does that code make sense to you? It doesn't to me. I can read it, sure, but the magic complicated things way too much. Just now, after the comment textbox scrolled down a bit and hid the 'iterator' together with the method definition, I'm left with a piece of code that doesn't make sense.

        Your suggestion is *exactly* the same. The compiler will reak havoc with your method, adding returns, changing it to a state machine. You *need* to specify where the returns are added so you're still in control of the function. Letting the compiler do whatever it wants doesn't look like a good idea to me at all.

      • earwicker
        October 31, 2010 at 12:35 am

        But there are countless ways in which the meaning of a line of code depends on the context. If you copy code from one place, paste it in another and find it doesn’t do the same thing, then complain the language is broken, then C# 1.0 was broken.

        To say that C# is a no-magic world is wrong. Lambdas and iterator blocks are both examples where a context is set up, and within that context the compiler does all kinds of magic – “normal” syntax doesn’t produce the same code as it would outside that context. Inside a lambda, adding a reference to a variable in the enclosing scope triggers a whole heap of transformations to the generated code. Suddenly, a “local” variable becomes something that might be visible to two threads, for example, because it is no longer stored on the stack.

        Your iterator example is misleading: iterators return several values, whereas as return i means “return a single value to the caller and don’t execute this method any further”. Clearly that would be an abuse of the return statement, so you’re right – but you’re beating a straw man you’ve constructed.

        There is no such mismatch in the case of asynchronous calls. A single value is returned, just as with a synchronous call. An ordinary method call implies “start an operation, wait for it to complete, get the return value”. A method that returns a Task has the ability to be called in the same way – that is, to be integrated into a larger operation by waiting for it to complete. In current versions of the language, there is no way to apply the same syntax to both kinds of methods consistently. The async modifier on methods could define a context in which the same uniform syntax can be used for “start, wait, get return value” on both kinds of method.

      • earwicker
        November 1, 2010 at 7:16 pm

        Re: your concern about changing the meaning of code between async and non-async contexts, I thought about this again today and considered using an attribute as an enabler for the new behaviour. And as well as massively simplifying it (and making it easier to explain) out of this comes a natural way to ensure that code could move between contexts without changing its meaning re: awaiting. I’ve put the details here:

        http://programmers.stackexchange.com/questions/15670/async-await-poll-for-alternatives/15796#15796

  2. February 16, 2012 at 6:45 pm

    I actually implemented WaitForClickAsync, Storyboard.BeginAsync and some other things if you want to take a look. I even mentioned this post in my article.

    http://labs.vectorform.com/2012/02/asynchronous-ui-development-in-winrt-silverlight-windows-phone-wpf-with-asyncawait-keywords-of-c-5-0/

    Get the sources at http://asyncui.codeplex.com/.

  1. February 16, 2012 at 6:10 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: