When you are dealing with an application that directly interacts with a network, or works on network resources, your app is bound to make some long running calls. If we were dealing with a good old Windows Forms application, the most important point was to avoid your application from blocking the UI. We would use some background threads or a background worker, while displaying a (relatively annoying) loading sign. We could extend this pattern by using some completed or error event handlers to notify the UI about the worker thread. The basic goal here is to let the user know that the application is doing some work in the background without blocking the UI thread, hence avoiding the “MyApp is not responding” status.
In Windows Store Apps, our job is a little harder. We have to deal with the whole “Fast and Fluid” ideology of windows runtime. In other words, our application should stay responsive at all times. We can still use some subtle indicators that the application is doing something in the background but we should avoid keeping the user waiting while we are finishing up a task. In this quest of ours to make our application responsive, our biggest helper is the new asynchronous API. With this new “framework”, you can implement/use progress callbacks, task abortion, awaiter’s and so on; by means of which you can throw some of the basic plumbing on windows runtime’s back.
In this post, we will be implementing and using some of the features of this asynchronous API. For this purpose lets create a new class that works on a simple eventing pattern. We will have a long running task, a Completed handler and an error event.
Preparing Our Demo Class
So let us start by defining our class with a long running task.
NOTE: In these examples, I will be using a custom diagnostic event listener for logging. You can the ETW logging implementation for WinRT sample from the MSDN Code Gallery.
public class EventingClass { private int DoTask1(int input) { int result; // Emulating the long running task Task.Delay(2000).Wait(); if (input < 0) { throw new ArgumentOutOfRangeException("input", "Input parameter cannot be smaller than 0"); } result = input + 1; return result; } }
So our operation just waits for 2000 milliseconds and if the input value is less than 0 throws an exception. Otherwise, it will return a result value. (NOTE: Task.Delay is very similar to Thread.Sleep function from the Threading namespace).
As we described above we need two events for success and error. So we first implement the event arguments:
public class MyTaskCompletedEventArgs : EventArgs { public int Result { get; private set; } public MyTaskCompletedEventArgs(int result) { Result = result; } } public class MyTaskFailedEventArgs : EventArgs { public string Message { get; private set; } public MyTaskFailedEventArgs(string message) { Message = message; } }
And finally we can implement the eventing pattern in our class:
public class EventingClass { public event EventHandler<MyTaskFailedEventArgs> OnTaskFailed; public event EventHandler<MyTaskCompletedEventArgs> OnTask1Completed; private void RaiseOnTask1Completed(int result) { var eventHandler = OnTask1Completed; if (eventHandler != null) { eventHandler(this, new MyTaskCompletedEventArgs(result)); } } private void RaiseOnTaskFailed(string message) { var eventHandler = OnTaskFailed; if (eventHandler != null) { eventHandler(this, new MyTaskFailedEventArgs(message)); } } public void DoTask1AsyncWithEvent(int input) { Task.Run(() => { try { var result = DoTask1(input); RaiseOnTask1Completed(result); } catch (ArgumentOutOfRangeException ex) { RaiseOnTaskFailed(ex.Message); } }); } }
The reason we are using the Task.Run function is the fact that this is the easiest way to execute our function on a separate ThreadPool on windows runtime. We are just emulating the background operation with an async thread.
So now our usage for this class and its functions would be something like:
var newEventingClass = new EventingClass(); newEventingClass.OnTask1Completed += (se, args) => CustomEventSource.Log.Debug("Task1 Completed Event"); newEventingClass.OnTaskFailed += (se, args) => CustomEventSource.Log.Debug("Task1 Failed Event"); CustomEventSource.Log.Debug("Task1 Starting"); newEventingClass.DoTask1AsyncWithEvent(0); Task.Delay(1000).Wait(); CustomEventSource.Log.Debug("Task1 Starting"); newEventingClass.DoTask1AsyncWithEvent(-1);
As a result of this execution we will have a log composed of:
2013-05-20 13:26:35:6462 Type: Verbose Id: 1 Message: ‘Here is the verbose message’
2013-05-20 13:26:35:6662 Type: Verbose Id: 1 Message: ‘Task1 Starting’
2013-05-20 13:26:36:6717 Type: Verbose Id: 1 Message: ‘Task1 Starting’
2013-05-20 13:26:37:6907 Type: Verbose Id: 1 Message: ‘Task1 Completed Event’
A first chance exception of type ‘System.ArgumentOutOfRangeException’ occurred in LocalStorage.exe
2013-05-20 13:26:39:2922 Type: Verbose Id: 1 Message: ‘Task1 Failed Event’
Great, we have an asynchronous pattern implemented using events. We can now move forward using some of the asynchronous API features.
Converting Events to Awaitables (TaskCompletionSource<T>)
One of the most useful features from the async framework is the TaskCompletionSource. Using the task awaiter developers can easily convert legacy async event implementations to awaitables. So in our example, we can actually write an additional method in our wrapper class to convert the execution for Task 1 to an awaitable.
private Task<int> ExecuteTask1(int input) { var newEventingClass = new EventingClass(); var myTaskSource = new TaskCompletionSource<int>(); // Subscribe to TaskCompleted: When the Task1Completed event is fired, set result. newEventingClass.OnTask1Completed += (sender, args) => myTaskSource.SetResult(args.Result); // Subscribe to TaskFailed: If there is an error in the execution, set error. newEventingClass.OnTaskFailed += (sender, args) => myTaskSource.SetException(new Exception(args.Message)); // Finally execute the task and return the associated Task promise. newEventingClass.DoTask1AsyncWithEvent(input); return myTaskSource.Task; }
As you notice from the comments, you have two main actions with the task completion source. On a successful call we set the result, on a failed call, we set the error / exception. And to actually call our method now, we can use all the features in an async awaitable call:
CustomEventSource.Log.Debug("Task1 Starting"); var taskResult = await ExecuteTask1(0); CustomEventSource.Log.Debug("Task1 Completed"); Task.Delay(1000).Wait(); try { CustomEventSource.Log.Debug("Task1 Starting"); var errorTaskResult = await ExecuteTask1(-1); } catch (Exception ex) { CustomEventSource.Log.Debug("Task1 Failed"); }
And as before, if we look at the log entries:
2013-05-20 13:43:47:8964 Type: Verbose Id: 1 Message: ‘Task1 Starting’
2013-05-20 13:43:49:9376 Type: Verbose Id: 1 Message: ‘Task1 Completed’
2013-05-20 13:43:50:9489 Type: Verbose Id: 1 Message: ‘Task1 Starting’
A first chance exception of type ‘System.ArgumentOutOfRangeException’ occurred in LocalStorage.exe
A first chance exception of type ‘System.Exception’ occurred in mscorlib.dll
2013-05-20 13:43:53:6639 Type: Verbose Id: 1 Message: ‘Task1 Failed’
Voilà! We converted the event based async implementation to an awaitable method.
Converting Event Pattern to Async Pattern (Task<T>>)
In the previous example, we refrained from changing the EventingClass. This class was maybe a legacy implementation, or there was quite a big amount of business logic implemented and it would be troublesome to implement awaitables.
However, what if you have access to EventingClass and the energy to rewrite the public access methods. In this case, let us get rid of all event related implementations and just deal with our async implementation.
public class EventingClass { public Task<int> DoTask1AsyncWithTask(int input) { return Task.Run(() => DoTask1(input)); } private int DoTask1(int input) { int result; // Emulating the long running task Task.Delay(2000).Wait(); if (input < 0) { throw new ArgumentOutOfRangeException("input", "Input parameter cannot be smaller than 0"); } result = input + 1; return result; } }
Our long running DoTask1 method is now executed as an awaitable. And just like in the previous example, you can call it with a simple try catch block.
Chaining / Batching Tasks (ContinueWith / WhenAll / WhenAny)
Let us now assume, we have three tasks (i.e. Task1, Task2, Task3), all receiving an input parameter of integer type and returning integer. Moreover, let’s assume we have to write a function that executes all of these methods in an awaitable fashion.
For this scenario, we set the following synchronous methods to be executed.
private int DoTask1(int input) { CustomEventSource.Log.Debug("Task1 Starting"); int result; // Emulating the long running task Task.Delay(2000).Wait(); result = input + 1; CustomEventSource.Log.Debug("Task1 Completed"); return result; } private int DoTask2(int input) { CustomEventSource.Log.Debug("Task2 Starting"); int result; Task.Delay(3000).Wait(); result = input + 2; CustomEventSource.Log.Debug("Task2 Completed"); return result; } private int DoTask3(int input) { CustomEventSource.Log.Debug("Task3 Starting"); int result; Task.Delay(4000).Wait(); result = input + 3; CustomEventSource.Log.Debug("Task3 Completed"); return result; }
If we were using the old eventing implementation we would have to chain the callbacks in the Completed events for each of these methods and handle the error. However with the ContinueWith<T> callback implementation we can easily chain these methods and have an awaitable parent function:
public Task<int> DoTasksAll(int input) { var task1 = Task.Run(() => DoTask1(input)); var task2 = task1.ContinueWith(task => DoTask2(task.Result)); var task3 = task2.ContinueWith(task => DoTask3(task.Result)); return task3; }
And when we call this method from our UI thread with an “await”, our log would show a result similar to (i.e. let’s say the initial input was 0):
2013-05-20 14:43:06:7226 Type: Verbose Id: 1 Message: ‘Here is the verbose message’
2013-05-20 14:43:06:7246 Type: Verbose Id: 1 Message: ‘TaskAll Starting’
2013-05-20 14:43:06:7336 Type: Verbose Id: 1 Message: ‘Task1 Starting’
2013-05-20 14:43:08:7439 Type: Verbose Id: 1 Message: ‘Task1 Completed’
2013-05-20 14:43:08:7459 Type: Verbose Id: 1 Message: ‘Task2 Starting’
2013-05-20 14:43:11:7609 Type: Verbose Id: 1 Message: ‘Task2 Completed’
2013-05-20 14:43:11:7649 Type: Verbose Id: 1 Message: ‘Task3 Starting’
2013-05-20 14:43:15:7660 Type: Verbose Id: 1 Message: ‘Task3 Completed’
2013-05-20 14:43:15:7680 Type: Verbose Id: 1 Message: ‘TaskAll Completed with result: 6’
In addition to ContinueWith, you are also equipped with functions like WhenAll and WhenAny. These functions are pretty self-explanatory. And the usage pattern would look something similar to the function declaration below if these tasks we have were mutually exclusive.
public Task<int[]> DoTasksAllBatch(int input) { return Task.WhenAll( Task.Run(() => DoTask1(input)), Task.Run(() => DoTask2(input)), Task.Run(() => DoTask3(input))); }
Notice the return values is an array of results we would get from the parallel execution of these tasks. So the log we would see from calling this method would be:
2013-05-20 14:54:00:2678 Type: Verbose Id: 1 Message: ‘TaskAllBatch Starting’
2013-05-20 14:54:00:2798 Type: Verbose Id: 1 Message: ‘Task1 Starting’
2013-05-20 14:54:00:2838 Type: Verbose Id: 1 Message: ‘Task2 Starting’
2013-05-20 14:54:00:2848 Type: Verbose Id: 1 Message: ‘Task3 Starting’
2013-05-20 14:54:02:2899 Type: Verbose Id: 1 Message: ‘Task1 Completed’
2013-05-20 14:54:03:3211 Type: Verbose Id: 1 Message: ‘Task2 Completed’
2013-05-20 14:54:04:3178 Type: Verbose Id: 1 Message: ‘Task3 Completed’
2013-05-20 14:54:04:3198 Type: Verbose Id: 1 Message: ‘TaskAllBatch Completed with result: 1,2,3’
If you look at the time codes, each task started at the same time and the final completion is fired when each of them completed.
I guess this is more than enough for today. In the next post we will be talking about Progress, Cancellation and the other async features of windows runtime.
Happy coding everyone….