Wrapping Other Asynchronous Patterns in Awaitable Tasks

July 22nd 2013 Async .NET Framework

Writing asynchronous code in .NET framework 4.5 is pure joy thanks to task-based asynchronous pattern (TAP) and the async/await syntactic sugar. Before we got to this point .NET introduced two other asynchronous programming patterns:

Although many APIs have been updated since the introduction of TAP to provide task based asynchronous methods which can be used with async and await, occasionally you will still encounter operations in APM or EAP without a TAP equivalent. Fortunately .NET framework provides helpers which will make wrapping older style asynchronous operations into tasks much easier.

APM operations can be wrapped using TaskFactory.FromAsync. It needs to be provided with the Begin* and End* method delegates, as well as all the input parameters for the Begin* method. The returned Task can be directly awaited to follow up the call with any cleanup operations that are required. Below is a sample wrapper for the Stream.BeginRead method (.NET framework 4.5 already provides a task-based alternative). More information about the wrapping is available on MSDN.

private async Task<byte[]> ReadAsync(string filename)
{
    var fileInfo = new FileInfo(filename);
    using (var stream = new FileStream(filename, FileMode.Open))
    {
        var buffer = new byte[fileInfo.Length];
        await Task<int>.Factory.FromAsync(stream.BeginRead,
            stream.EndRead, buffer, 0, buffer.Length, null);
        return buffer;
    }
}

Wrapping EAP operations is a bit more complex. In this case TaskCompletionSource<T> needs to be used. Its methods SetException, SetCanceled and SetResult allow the forwarding of callback event argument properties to the task created by TaskCompletionSource. It will be easier to understand by reading the example below, wrapping WebClient.DownloadStringAsync (.NET framework 4.5 again provides a task-based alternative). Refer to MSDN for more details.

private Task<string> DownloadStringAsync(Uri uri)
{
    var taskSource = new TaskCompletionSource<string>();

    var webClient = new WebClient();
    webClient.DownloadStringCompleted += (sender, args) =>
    {
        try
        {
            if (args.Cancelled)
            {
                taskSource.SetCanceled();
            }
            else if (args.Error != null)
            {
                taskSource.SetException(args.Error);
            }
            else
            {
                taskSource.SetResult(args.Result);
            }
        }
        finally
        {
            webClient.Dispose();
        }
    };
    webClient.DownloadStringAsync(uri);

    return taskSource.Task;
}

Thanks to these wrappers there really is no reason to implement asynchronous calls in any other way than by using async and await, unless you need to support older framework versions.

Copyright
Creative Commons License