| Windows XAML in Action
By Pete Brown
Metro style applications built on the Windows Runtime are especially sensitive to any delays in the UI thread. Users will immediately notice when the UI trails their touch input and “doesn’t stick to their finger.” Animations that stutter and skip are immediately visible. Any lag in processing negatively affects the experience and the perception of not only your application, but the platform as a whole. A good way to help ensure your application doesn’t get into this state is to make long-running calls asynchronous. In this article, based on chapter 3 of Windows 8 XAML in Action, author Pete Brown discusses the Windows Runtime approach to asynchronous code.
You may also be interested in…
|
The Windows Runtime was built with the concept of asynchronous
operations. It was also built with the idea that these asynchronous operations
must work not only for C#, but also for JavaScript, C++, and any number of
other, very different languages. For those reasons, the team didn’t take a
dependency on the Task Parallel Library in .NET, but, instead, created an
interface-based approach consistent with the rest of the Windows Runtime.
The Windows.Foundation
namespace contains the types and
interfaces required to support asynchronous functions in WinRT. Every
asynchronous operation implements, at a minimum, the IAsyncInfo interface. Like most
programming problems, there is an easy but limited way to do async and a more
complex but capable way.
In this article, we’ll look at the different approaches for using
the WinRT asynchronous APIs; that is, the ones which return IAsync*
interfaces. I’ll cover the easiest approach and then look into what’s needed to
check progress or cancel an asynchronous operation.
Async and Await: The Simplest Approach
The simplest and most commonly used approach is to use the await keyword. To
use an awaitable function, it must be used in a function marked as async, and then
all you need to do is use this keyword and call it like any normal synchronous
function, as shown in listing 1.
Listing 1 Using the Await Keyword to Perform a
Network Operation
protected override void OnNavigatedTo(NavigationEventArgs e)
{
LoadFeedUsingAwait(); #A
}
private async void LoadFeedUsingAwait() #B
{
var client = new SyndicationClient();
var uri = new Uri("http://feeds.feedburner.com/PeteBrown");
var feed = await client.RetrieveFeedAsync(uri); #C
Debug.WriteLine(feed.Title.Text);
}
#A Function calls will go here
#B Method marked as async
#C Async call using await
The code in listing 1 goes in the code-behind for the main page
on a new application. Note how the function is called from OnNavigatedTo
. Run
the application and (on a single display system) switch back to your IDE and
look at the output window. You should see the title of my blog displayed there.
On a dual display system, the Metro style app will show up on one display and
the IDE on the other, so no switching is required.
Tip
Everyone
knows that if it’s printed, it immediately has capital-A Authority, so here’s
my little gift to you. Copy this paragraph and send it to your manager:
Dear developer bosses of the world: Metro style development on
x86/x64 hardware needs developer machines with at least two displays to be
effective. A second display is incredibly inexpensive, even if you purchase one
of those snazzy 3M multi-touch displays. Without the second display, your
developers will waste precious hours just in context switching between Metro
and the desktop during debugging sessions. You’ll save time, money, and
headaches by properly equipping your entire team.
Marking your methods with the async modifier causes the compiler to
reconstruct the method, inserting possible suspend and resume points every
place where you use the await
operator. The compiler ensures all the state management is handled for you, so
you don’t need to really think about the fact that your method may have stopped
running. In order to use the await operator, the method must be marked as async.
Using the await
operator makes it really easy to support making asynchronous calls—all the
magic required to support asynchronous operations is wrapped up in code you
never see. This approach works well for the majority of situations. However,
when you want to check the progress of an operation or cancel an operation
in-progress, you must use a slightly more complex approach.
Async and Await Under the Covers
When you use async and await in your code, the compiler does a lot on your
behalf. Any function which is marked using the async modifier is rewritten as a state
machine. All local function state is stored in a structure.
The code that implements the actual functionality you wrote is
also located in a method named MoveNext on that same structure. Local variables become
fields in the structure. Your original function ends up being just a bit of a
shell which initializes the state machine and then passes control to it.
The implementation code starts with a switch statement which
uses the current state as input. Depending upon that state variable, the code
branches to one of the asynchronous calls, a check to see if the calls have
completed and code to get the results, or the end of the function.
This state machine enables the function to be suspended and
the state boxed on to the heap for reactivation once the asynchronous call
returns.
Each completed step modifies the state. Each time the function
is called, the opening switch statement branches the code so it picks up where
it previously left off.
The end result is a lot of compiled code created on your
behalf. If you’re curious about the inner workings of the asynchronous state machine, you can see the generated MSIL (Microsoft
Intermediate Language) and more in my blog post on this topic: http://bit.ly/asyncstatemachine
Long-Form Asynchronous Operations
The async
and await keywords are conveniences, but aren’t required when working with
asynchronous operations. You can still use the long-form approach with event handlers
and callback functions.
The options available to you depend upon which interfaces the
return type of the asynchronous function implements. Table 1 shows the
different interfaces and describes their capabilities.
Interface | Description |
IAsyncAction | The most basic async support interface. This defines an
asynchronous method which does not have a return type. |
IAsyncActionWithProgress<TProgress> | An interface which supports progress reporting of a
supplied type. |
IAsyncOperation<TResult> | Interface which supports an asynchronous method which has
a return value. |
IAsyncOperationWithProgress<TResult,TProgress> | Interface which supports an asynchronous method with both
a return value, and progress reporting. |
Table 1 Async support interfaces in WinRT. All
asynchronous operations must return a type implementing at least one of these
interfaces.
In addition, the IAsyncInfo
interface supports the asynchronous operations
by providing Cancel
and Close
methods, as well as get error and status information. The four interfaces
include this capability as they inherit from IAsyncInfo.
You can also use the long form approach to make the call. In this
case, the return type implements the most feature rich of the interfaces: IAsyncOperationWithProgress
.
That means when working with the SyndicationClient
, we can get back both a result, but also
get updates as the operation progresses. This is the signature:
public IAsyncOperationWithProgress<SyndicationFeed,RetrievalProgress>
RetrieveFeedAsync(System.Uri uri)
SyndicationFeed
is the real return type—it’s the data you want. If this function were
implemented without asynchronous operation in mind, it might look like this:
public SyndicationFeed RetrieveFeedAsync(System.Uri uri)
But it’s asynchronous, so the return type is a class
implementing the interface. Listing 2 shows how to call the function using the
long form asynchronous pattern. Call this function from OnNavigatedTo
just as we did in listing 1.
Listing 2 Downloading a Syndication Feed Using the
Long Form Approach
private void LoadFeedUsingLongForm()
{
var client = new SyndicationClient();
var uri = new Uri("http://feeds.feedburner.com/PeteBrown");
var op = client.RetrieveFeedAsync(uri);
op.Completed = (info, status) => #A
{
switch (status)
{
case AsyncStatus.Canceled: #B
Debug.WriteLine("Operation was canceled");
break;
case AsyncStatus.Completed: #C
Debug.WriteLine("Operation was completed");
Debug.WriteLine(info.GetResults().Title.Text);
break;
case AsyncStatus.Error: #D
Debug.WriteLine("Operation had an error:");
Debug.WriteLine(info.ErrorCode.Message);
break;
}
};
}
#A Completed delegate
#B Operation canceled
#C Operation complete
#D Operation error
Notice how Completed
is not an event handler. Instead, it is simply a delegate property. For that
reason, you use = rather than += when adding your handler code. This subtle
difference could trip you up if you don’t recognize it right off the bat.
When you run this, the Completed delegate will be called, which
will then write out the title of my blog in the debug window in the IDE. As
before, you’ll need to terminate the application from the IDE.
What about race conditions?
If you look at the code in listing 2, you may wonder about a
possible race condition between executing the function and wiring up the
Completed handler.
In regular .NET asynchronous code following the event
approach, you need to wire up the event handlers before executing the
operation. This is to ensure that the event handlers are available before the
function needs them. For potentially fast async operations, this is especially
important.
In the Windows Runtime, however, the operation begins execution
as soon as you call the function. There is no way for you to wire up a handler
in between declaration and execution. Could there be a race condition there
where the operation completes before the handler is available?
Luckily, the Windows Runtime team thought of this very
situation, and ensured it will not happen. The returned operation contains
everything that is needed for asynchronous function management. If the function
call completes before you wire up the handler, the operation is still around and
is able to manage context and handlers. In that scenario, as soon as you make
the handler available, the operation will call it.
So, you don’t need to worry about race conditions with setting
up your handlers. The team already did.
This same thought was put into Task for functions such as ContinueWith.
Knowing when the task is complete is essential. For some
long-running tasks, you may want to get some information about interim status.
Getting Progress Updates
Some especially long tasks, such as downloading a large file
over a slow connection or processing a large amount of data, should report back
progress information to the calling code. This becomes especially important in
the tablet market where bandwidth varies greatly from location to location.
To support flexible progress reporting, the IAsyncActionWithProgress
and IAsyncOperationWithProgress
interfaces provide a way for an asynchronous function to provide status updates
using whatever data format is appropriate for that function.
In this case, the reporting type is a RetrievalProgress
structure which exposes
two properties: bytesRetrieved
and totalBytesToRetrieve.
Using these two properties, you can figure out the percentage complete and
percentage left. Listing 3 shows how.
Listing 3 Obtaining Progress Reports from the RSS
Feed Downloader
private void LoadFeedUsingLongForm()
{
var client = new SyndicationClient();
var uri = new Uri("http://feeds.feedburner.com/PeteBrown");
var op = client.RetrieveFeedAsync(uri);
op.Completed = (info, status) =>
{
... #A
};
op.Progress = (info, progress) => #B
{
Debug.WriteLine(string.Format("{0}/{1} bytes {2:P}",
progress.bytesRetrieved,
progress.totalBytesToRetrieve,
(float)progress.bytesRetrieved /
(float)progress.totalBytesToRetrieve));
};
}
#A Code in previous listing
#B Progress report
This code is identical to listing 2, with the addition of a Progress handler.
During execution, at a frequency determined by the code you’re calling, you’ll
receive progress updates through this handler.
When I ran the above code, however, I got some pretty interesting
results. The totalBytesToRetrieve
was incorrect, reporting a meaningless number. If I changed the URI to http://10rem.net/blog/rss.aspx[1] instead, I got the correct results:
4096/205187 bytes 2.00 %
8192/205187 bytes 3.99 %
12288/205187 bytes 5.99 %
16384/205187 bytes 7.98 %
[snip]
200704/205187 bytes 97.82 %
204800/205187 bytes 99.81 %
205187/205187 bytes 100.00 %
Operation was completed
Pete Brown's Blog (POKE 53280,0)
Not all services correctly report the total bytes. In this case,
Feedburner clearly does not. When performing your own download, you’ll need to
check that number and adjust your progress reporting appropriately. One way
would be to show an indeterminate progress bar in the case of unreported total
bytes.
What about long-running operations that you need to abort? How
would you deal with that situation?
Cancelling the Operation
For especially long-running operations, such as downloading a
file, you’ll want to provide a way for the user to cancel. When I’ve had to do
this in the past, I would provide a "cancel" button, which would then set a
flag. The code doing the long running operation, typically in a loop or timer, would
check this flag during each iteration. If the cancel flag was set, the
operation would terminate and clean up after itself.
When working with asynchronous code in the Windows Runtime, the
process is not all that different from the approach I described. However,
instead of setting a flag, you call the Cancel function on the operation. The
executing code uses this to determine what steps to take.
Listing 4 is an update to listing 3, which will cancel after a
set number of bytes have been downloaded. We haven’t yet covered controls and
the UI, so I don’t want to deal with buttons and whatnot just yet.
Listing 4 Cancelling the Operation After 5,000
Bytes have been Downloaded
bool alreadyCancelled = false; #A
op.Progress = (info, progress) =>
{
Debug.WriteLine(string.Format("{0}/{1} bytes {2:P}",
progress.bytesRetrieved,
progress.totalBytesToRetrieve,
(float)progress.bytesRetrieved /
(float)progress.totalBytesToRetrieve));
if (!alreadyCancelled) #A
{
if (progress.bytesRetrieved > 5000)
{
info.Cancel(); #B
alreadyCancelled = true;
}
}
};
#A Prevent redundant Cancel calls
#B Cancel operation
This listing shows one approach to cancelling. Typically, you’d
use a button or other user interface device to call the Cancel method, as
stated above.
One important thing to note from this listing is that, when you
run it, you may not see it actually cancel until the operation is complete.
Remember, this is an asynchronous operation, and the function is responsible
for deciding when it can cancel, and how quickly to do so. For grins, if you
change the 5000 to 0, you’ll likely see it cancel right away.
Looking at these various long-form approaches to asynchronous
code may make you double-check to see if you accidentally hit 88 mph in a
DeLorean while you were napping. It certainly may feel like we’ve gone
backwards, but, with the addition of async and await now making this verbose approach
optional, I can assure you that we’re going in the right direction.
If you want the flexibility and power, choose the long/verbose
version. If you don’t need all that flexibility (which will likely be the
majority of the time—do you really need progress reporting or cancellation when
opening a file?), use the async
and await
keywords to make your life easier. Don’t force yourself or your team to pick
just one approach; use what works for the specific situation and keep it as
simple as possible as often as possible.
Summary
Over the past years, asynchronous operations have become a fact
of life for developers. I blame Ajax[2]
and JavaScript for it, but, then again, I generally blame those two for all the
things I dislike. No, what’s really to blame here is the expectations of the
user. There was a time when it was perfectly acceptable to have your
application lock up and be unresponsive during a long calculation or other
operation. Many applications would hang when doing something as simple as
printing.
But then we started adding in animation, and not just the cute
dancing baby type of animation made popular on GeoCities and Angelfire, but
animation that is both useful and conveys information. Operations which caused
the animation to stutter or skip suddenly became a real problem. For browsers,
and browser plug-in technologies like Silverlight, and even desktop
technologies like WPF, the recommended networking (the biggest latency issue by
far) approach was to use asynchronous code. Browsers and Silverlight required
it, WPF didn’t. The result was clear, if it wasn’t required, it didn’t happen.
Finally, the straw that broke the camel’s back was touch
interaction. Touching a UI is such an immediate and deep interaction, that any
sort of lag in responsiveness becomes immediately noticeable. When you put apps
that rely on touch, on a tablet designed for touch, using a relatively
low-power ARM processor, any and all lag becomes immediately noticeable.
Importantly, users who see the lag don’t generally think "wow, that app
stinks", but instead think "wow, this tablet stinks". So, Microsoft made the
decision to make anything even remotely slow into an asynchronous API.
This was the right decision, and because of the addition of the async and await keywords, it
also became a decision that is really easy to swallow. Unless you want to take
on the added complexity because you need to report progress, or provide an
option to cancel an operation, you can simply use the helpful keywords and
forget all about the operation being asynchronous at all.
The Windows Runtime includes asynchronous methods all over.
Additionally, the .NET libraries used for writing C# Metro style applications
also include asynchronous code. However, those libraries use the Task class rather
than the WinRT interface-based asynchronous approach. This helps keep .NET
consistent with the desktop version, and also with the previous version of
.NET. Gladly you can use async
and await
just as easily with a .NET Task
as you can with a WinRT IAsync*
interface. Plus, if you really want to keep your code consistent, you can
easily convert between the two approaches using included extension methods.
Asynchronous code used to be something we
dreaded in application development. With both Task and IAsync*, and the new async and await keywords,
combined with the appropriate use of asynchronous operations throughout the
libraries, I feel like, for the first time, we really have async done right.
Async everywhere? I’m cool with that.
Here are some other Manning titles you might be
interested in:
[1] My source RSS feed, which I use to feed the far more scalable and reliable Feedburner feed
[2] And you too, jQuery! I know what game you’re playing.