Using Closures to Simplify Asynchronous Programming

Silverlight Logo

When I teach Silverlight, some of the students lament at the requirement to make all network requests in an asynchronous manner. While I explained my opinion on the manner back in March of this year, I didn't get into how to make this easier. Lambdas and closures are the key to simplifying that code in my opinion. 

Quick caveat!  This does not work in Visual Basic (yet). VB10 has announced support for closures and lambdas that do not return values (e.g. Sub() instead of Function()), if you are using Visual Basic, I can't help you make it easier until VB10 ships.

Let's take the example of making a call to a simple web service.  I will show you two examples to try and get my idea across.  First we have a simple web service that returns a list of Game objects based on a single criterion (the genre).  Calling this service is simply a matter creating the client, registering the event handler and calling the asynchronous method:

// Create the client
GamesClient client = new GamesClient();

// Register the handler
client.GetGamesCompleted += 
  new EventHandler<GetGamesCompletedEventArgs>(client_GetGamesCompleted);

// Start the operation
client.GetGamesAsync("Shooter");

In the event handler we can just check the errors and set the result to our UI:

void client_GetGamesCompleted(object sender, GetGamesCompletedEventArgs args)
{
  // Complete the operation
  if (args.Error == null && !args.Cancelled)
  {
    theList.ItemsSource = args.Result;
  }
}

This code is pretty simple but we can use a lambda to avoid littering our code with unnecessary methods:

// Create the Client
GamesClient client = new GamesClient();

// Handle the event with a Lambda
client.GetGamesCompleted += (source, args) =>
{
  if (args.Error == null && !args.Cancelled)
  {
    theList.ItemsSource = args.Result;
  }
};

// Execute the Service Call
client.GetGamesAsync("Shooter");

Using the lambda (=>) creates an inline anonymous method for handling of the event.  Since this will only be used by our client and the client is short-lived (see issues with anonymous methods and long-lived objects here), this works well to simplify the code. 

This code becomes more difficult if you have to pass it data as developers tend to move that data to instance level (e.g. class level) variables to share it with the resulting code.  In other places (like AsyncCallback objects), they allow you to pass in a payload of data.  But this ends up with pretty ugly code.  For example, lets use the HttpRequest style of this same code;

// Interface to the HttpWebRequest-style interface
Games request = new GamesClient() as Games;

// Make the request
request.BeginGetGames("Shooter",
                      new AsyncCallback(OnGetGamesComplete), 
                      request);

In this style of the request, you would use a AsyncCallback to specify a method to callback and then pass in some state (in this case the request we made so we can use it in the callback. This results in the callback having to do some casting to get our object back;

void OnGetGamesComplete(IAsyncResult result)
{
  // Cast the passed in state
  Games theRequest = (Games)result.AsyncState;

  // Get the games
  IEnumerable games = theRequest.EndGetGames(result);
}

This grabbing of the state always annoyed me, but I figured it was necessary.  Or is it.  In this case our use of a lambda allows us to use a feature called closures.  With closures you can see local variables declared outside the anonymous method inside the method. For example:

// Interface to the HttpWebRequest-style interface
Games request = new GamesClient() as Games;

// Make the request
request.BeginGetGames("Shooter",
  new AsyncCallback(
    (result) =>
      {
        // Get the results
        IEnumerable<Game> games = request.EndGetGames(result);
      }
    ), null);

In this example we can use the request object create outside the anonymous method because the closure allows this.  This really simplifies the code. In fact, since the HttpRequest style is called on a non-UI thread, we can nest the lambdas and closures to call to the UI thread (using the Dispatcher, see my post on that here):

// Interface to the HttpWebRequest-style interface
Games request = new GamesClient() as Games;

// Make the request
request.BeginGetGames("Shooter",
  new AsyncCallback(
    (result) =>
      {
        // Get the results
        IEnumerable<Game> games = request.EndGetGames(result);

        // Use another Lambda and closure to update the list on the UI thread!
        Dispatcher.BeginInvoke( 
          () =>
            {
              theList.ItemsSource = games.ToList();
            }
          );
      }
    ), null);

Hopefully, this will help you in using the asynchronous model that is required in Silverlight 2. Note that this is the same as it is for non-Silverlight projects, but I find it really useful for students that have not had the opportunity to do asynchronous communication in their desktop applications.

You can download the code and play with it yourself.  Each of these methods are show in the project's page.xaml.cs file (see the #defines at the top of the file to switch between different versions):

AsynchronousClosures.zip

Comments:

Gravatar

This is a particularly good post, even for someone who posts such good stuff consistently. I was hip to Lambdas for this sort of stuff but not the closure - very enlightening

Gravatar

Can you include 3rd party assemblies in your examples ? (Perhaps a 'dependencies' or 'lib' folder inside the project)

ie. Microsoft.Windows.Controls.dll

Thanks for the great post/sample

Gravatar

Another note while on topic of asynchronous programming in Silverlight:

ie. ajax, Adobe Flex, etc... all make it fairly brainless to provide a 'loading' animation while a asynchronous call is in progress rather than having the user just sit there wondering what is happening. Typically covering the area in which data is being loaded in.

It would be good to see this done as a best practice even in samples. (Seems rather incomplete without this vital user experience covered)

Thanks again.

Gravatar

Lastly... (sorry bout that) - nothing is showing up on the page for me - I step into the code, but the page is 'empty'

Gravatar

Steve,

I've updated the .zip file to include the 3rd party DLL's. Please see if it continues to be empty for you.

Gravatar

Steve,

Concerning Flex and AJAX making it easy to show a progress to the user, I am not sure what you mean. You can use the ProgressBar or simply have an animation that you "Begin()" when you start your call to show the user that you are busy. I have an example of one way of doing this on http://www.silverlightdata.com.

Gravatar

Hey Shawn - while closures are handy for this, they need to be used with caution. Trouble occurs when you reference a locally scoped variable, because what really happens is the compiler hoists the variable to an anonymously named member field of the class. This works OK until you have concurrent requests running. In other words - call the code that relies on closures a few times concurrently (so they overlap), and the "request" variable will get clobbered because you are effectively using a shared field for multiple async operations without any sharing safeguards (classic race scenario). The safest approach is to either control the hoisting by implementing an explicit state machine class, or to always use the asyncState mechanism instead of relying on a closure.

Gravatar

Good info Keith. So is this safe for non-overlapping requests (e.g. simple scenario's).

Gravatar

Yes, safe for that (if you can guarantee non-overlapping calls).

So if the async work is being done in response to a button click (for example), then first order of business should be to disable the button before starting the async call, and then re-enable upon callback of the chained lambda. Re-entrancy of the async code needs to be prevented.

Gravatar

Great comment...thanks Keith!

Gravatar

Understandably, the sample is just some concept illustration, but what I found disappointing was inconsistent and different behaviour from browser to browser in my case.
1. Didn't work at all in Firefox. Hanging in "transferring data from localhost"
2. Google Chrome was the most stable ignoring errors ( e.g. for #define USE_WEBCLIENT mode some titles were greyed out , no image appeared ).

3. Internet Explorer
Problems with moving focus in/out IE window which shuts down IE for all modes. ( not sure whether any relevancy I do 2 monitors )
The program '[3276] iexplore.exe: Silverlight' has exited with code -1073741819 (0xc0000005).
The program '[2028] WebDev.WebServer.EXE: Managed' has exited with code 0 (0x0).
=========================
Mode #define USE_WEBCLIENT ( error on page which probably was ignored by Chrome )
Line: 454
Char: 17

Error: Sys.InvalidOperationException: ImageError #4001 in control 'Xaml1': AG_E_NETWORK_ERROR

Gravatar

Michael,

Not sure the problem you are having. I see the Firefox issue and I haven't figured out yet why that is.

But for the 4001 error, this occurs when an image isn't found (and I am not catching it) so either XBox.com (where the images occurred) is down or it has to do with something else. The focus issue in IE is odd and I can't replicate it. Also, what part of the world are you? I've found some odd localization issues with Silverlight.

Gravatar

Shawn,
I tried on other computer , OS was different -Vista 64, IE focus issue has gone. It might be computer specific for my configuration with Windows XP SP2 and dual monitors and running VPN and RDP and moving mouse between RDP maximised screen on other monitor and Silverlight running at home computer or something else. I figured out datetime parsing issue which failed for me in Australia.


 



 
Save Cancel