Metro Nuggets: Async is your friend

Tuesday, March 20, 2012 2:15:04 PM Categories: WinRT/WIn8-Metro WP7

I’m in the process of exploring the Metro-Style Win8 API. If you’ve done much looking you probably have discovered that the new Async is everywhere! This is not a bad thing as your code is much cleaner and easier to read. The downside though is that creating something that is compatible between say Win8 Metro and Windows Phone (the original metro) can be difficult.

Luckily there is still a CTP of the Async stuff that works with Windows Phone (and it has a GoLive license, so you can use it). CTP sounds scary (it did to me too). If you are deciding to do install the Async CTP, you might want to go here --http://blogs.msdn.com/b/lucian/archive/2011/11/01/async-ctp-v3-installation.aspx-- and read up.. since I am betting you –like I-- have some VS patches that will prevent you from installing it). To get it installed you need to temporarily uninstall the Silverlight 5 tools and any Visual Studio patches after say December.. if you look at the link it will get you started (there are a couple forum posts that were helpful to me).

Now you are ready to play. I’m sure by now you have seen the syntax, but let’s make some comparisons (and we’ll use WIndows Phone code and an example on a class that I have rarely seen discussed: HttpWebRequest):

Here’s the old way (warning there is a lot of code here):

   1: // Pretend like Item info below just contains a couple pieces of info:
   2: //  like Uri, Method, and possible a filename to save to --in IsoStorage
   3: void downloadSomething(UriInfo item) { 
   4:     var webReq = HttpWebRequest.CreateHttp(UriInfo);
   5:     webReq.Method = item.UriMethod;
   6:     WebRequests.Add(item, webReq);
   7:     // If we were passing post values to the routine we would need to 
   8:     // Begin getting the request stream with a callback and then End Getting 
   9:     // the request stream (this is bad enough.. don't want to overcomplicate things)
  10:     webReq.BeginGetResponse(ResponseCallback, item);
  11: }
  12: protected void ResponseCallback(IAsyncResult ar) {
  13:     IFileItem item= (IFileItem) ar.AsyncState;
  14:     var webR = WebRequests[item];
  15:     var resp = (HttpWebResponse) webR.EndGetResponse(ar);
  16:  
  17:     if (resp.StatusCode == HttpStatusCode.OK) {
  18:         // We could also do the whole Begin/End on the Response Stream 
  19:         // (with another calllback)
  20:         using (var strm = resp.GetResponseStream()) {
  21:             var buffer = new byte[4096];
  22:             item.BytesDownloaded = 0;
  23:             // If the file exists we need to delete it (BTW, using Jay's IsolatedStorage Facade which looks like System.IO.File, Directory, FileInfo, and DirectoryInfo -- "PS" stands for persisted storage)
  24:             if (PSFile.Exists(item.DestinationNameAndPath))
  25:                 PSFile.Delete(item.DestinationNameAndPath);
  26:  
  27:             // We should actually check to see if the path exists for this file.. 
  28:             // BTW, this uses my Isolated Storage Facade classes 
  29:             // (so PSDirectory == SystemDirectory, but just against IsolatedStorage)
  30:             if (!String.IsNullOrEmpty(System.IO.Path.GetDirectoryName(item.DestinationNameAndPath)) && !PSDirectory.Exists(System.IO.Path.GetDirectoryName(item.DestinationNameAndPath)))
  31:                 PSDirectory.CreateDirectory(System.IO.Path.GetDirectoryName(item.DestinationNameAndPath));
  32:  
  33:             using (var fs = PSFile.Create(item.DestinationNameAndPath)) {
  34:                 var fileLength = item.ActualSize;
  35:                 do {
  36:                     int count = strm.Read(buffer, 0, 4096);
  37:                     if (count < 1)
  38:                         break;
  39:                     fs.Write(buffer, 0, count);
  40:                     item.BytesDownloaded += count;
  41:                 } while (true);
  42:             }
  43:             item.BytesDownloaded = item.ActualSize;
  44:         }
  45:     }
  46:     //else 
  47:         // We got an error or something. Normally I notify the end user of this (this is left up to you to do on your own)
  48:  
  49: }

New Way:

   1: public static async Task<string> simpleHttpPost(string url)
   2: {
   3:     var uri = new Uri(url);
   4:  
   5:     HttpWebRequest client = HttpWebRequest.CreateHttp(uri);
   6:     client.Method = "POST";
   7:  
   8:     var response = (HttpWebResponse) (await client.GetResponseAsync());
   9:     var _lastResultCode = response.StatusCode;
  10:  
  11:     if ((int)_lastResultCode < 400) {
  12:     using (var strm = (new StreamReader( response.GetResponseStream())))
  13:         return await strm.ReadToEndAsync();
  14:     }
  15:  
  16:     return "";
  17: }

Admittedly when giving examples you try to keep the code similar for comparison sake. I decided to simply grab some code from my new project and from an old project. Please forgive me. I will do my best to highlight the big differences here.

The old way with HttpWebRequest was that you did a Begin____(callbackFunction) for nearly everything. The callback function must (among other things) call End______() to actually get what you were looking for (this could be the RequestStream, so you could make a post and push post variables, or it could be the response, or the ResponseStream – the ResponseStream at least has a synchronous version which I used above). Because this was going on you could use delegates and not lose context, but the execution order was jumbled.. so essentially you created a delegate for each callback (so the code came first) then you would call the Begin which would go out do the activity and then make calls into your code that was previously defined.. it is very ugly and hard to follow what is happening…

The new way introduces the use of the await or Await (in VB) keyword. When I first saw this I thought it was a gimmick, but the reality is that our APIs can be cleaner. You get the ability to simply suspend execution of your code while you wait for something to come back. You do have to mark your function with the async (or Async in VB) Keyword and use Task<T> as a result.

I really feel like the second example is much cleaner and easier to follow. At the very least you know what order things are happening..

An Added Benefit
Let’s make a not so contrived example to show you something else that is pretty cool. Let’s assume that we have an app whose first action is to run out to a service of some sort and get some data that will be databound to the UI. Meanwhile we have a few additional things that need to be done (so we don’t want to be prevented by the download)..

   1: protected void setupMainPage() {
   2:     getData();
   3:     // Next line will happen before the getData call fully completes
   4:     // this is because we are not "await"ing the return
   5:     doSomethingElse();
   6: }
   7: protected async void getData() {
   8:     var result = await simpleHttpPost(new Uri("http://someDomain.com/someService"));
   9:     var dataObject = deserializeObject(result); // turns json string into object
  10:     this.DataContext = dataObject;
  11: }

So essentially we can fire things off and when they complete they will continue running. We don’t have to await everything.

Preview of Future posts
At present I am building a new Dropbox client library for Windows8 and am retrofitting it for Windows Phone. To do this I had to create some compatibility objects for Windows Phone to make this happen. I actually also had to create some helper objects that do things like HTTP because they are very different and I wanted to avoid compiler directives as much as is possible. I will be writing about all of this in the coming weeks. I will eventually be speaking at a combines Tampa Bay Windows Phone UG and Tampa Bay Windows Dev UG meeting about all of this.

Comments

Comments are closed on this post.
Site Map | Printable View | © 2008 - 2014 Intradynamics | Powered by mojoPortal | HTML 5 | CSS | Design by mitchinson