C# 5 Async, Part 3: Preparing Existing code For Await
While the Visual Studio Async CTP provides a fantastic model for asynchronous programming, it requires code to be implemented in terms of Task and Task<T>. The CTP adds support for Task-based asynchrony to the .NET Framework methods, and promises to have these implemented directly in the framework in the future. However, existing code outside the framework will need to be converted to using the Task class prior to being usable via the CTP.
Wrapping existing asynchronous code into a Task or Task<T> is, thankfully, fairly straightforward. There are two main approaches to this.
Code written using the Asynchronous Programming Model (APM) is very easy to convert to using Task<T>. The TaskFactory class provides the tools to directly convert APM code into a method returning a Task<T>. This is done via the FromAsync method. This method takes the BeginOperation and EndOperation methods, as well as any parameters and state objects as arguments, and returns a Task<T> directly.
For example, we could easily convert the WebRequest BeginGetResponse and EndGetResponse methods into a method which returns a Task<WebResponse> via:
Task<WebResponse> task = Task.Factory
.FromAsync<WebResponse>(
request.BeginGetResponse,
request.EndGetResponse,
null);
Event-based Asynchronous Pattern (EAP) code can also be wrapped into a Task<T>, though this requires a bit more effort than the one line of code above. This is handled via the TaskCompletionSource<T> class. MSDN provides a detailed example of using this to wrap an EAP operation into a method returning Task<T>. It demonstrates handling cancellation and exception handling as well as the basic operation of the asynchronous method itself.
The basic form of this operation is typically:
Task<YourResult> GetResultAsync() { var tcs = new TaskCompletionSource<YourResult>(); // Handle the event, and setup the task results... this.GetResultCompleted += (o,e) => { if (e.Error != null) tcs.TrySetException(e.Error); else if (e.Cancelled) tcs.TrySetCanceled(); else tcs.TrySetResult(e.Result); }; // Call the EAP-based asynchronous method this.GetResult(); // Return the task from the TaskCompletionSource return tcs.Task; }
We can easily use these methods to wrap our own code into a method that returns a Task<T>. Existing libraries which cannot be edited can be extended via Extension methods. The CTP uses this technique to add appropriate methods throughout the framework.
The suggested naming for these methods is to define these methods as “Task<YourResult> YourClass.YourOperationAsync(…)â€. However, this naming often conflicts with the default naming of the EAP. If this is the case, the CTP has standardized on using “Task<YourResult> YourClass.YourOperationTaskAsync(…)â€.
Once we’ve wrapped all of our existing code into operations that return Task<T>, we can begin investigating how the Async CTP can be used with our own code.
Hello Reed,
Why have you tagged your C# 5 articlles with “C# 4” tag?
(I am interested to find and run code in C# 4 but not C# 5)
When I wrote this, it was using the CTP – and was C# 4. I’ll adjust it now. Note that you can target .NET 4 using Microsoft.Bcl.Async (but it still requires the C# 5 compilers) available from http://nuget.org/packages/Microsoft.Bcl.Async/
Hi, I tried your basic form and it works with one caveat. For some reason, when I call it with “await” it never waits. The only way I could get it to work is to sneak in tcs.Task.Wait() which obviously defeats the purpose. Any idea what could be going wrong?
Unfortunately, it’s tough to tell without seeing your code. Want to paste in your wrapper code here? I’ll take a look.