This situation is where Task
, async
, and await
really shine. Here's the same example, refactored to take full advantage of async
(it also uses some helper classes from my AsyncEx library to clean up the mapping code):
// First, a base class that takes care of the Task -> IAsyncResult mapping.
// In .NET 4.5, you would use HttpTaskAsyncHandler instead.
public abstract class HttpAsyncHandlerBase : IHttpAsyncHandler
{
public abstract Task ProcessRequestAsync(HttpContext context);
IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
var task = ProcessRequestAsync(context);
return Nito.AsyncEx.AsyncFactory.ToBegin(task, cb, extraData);
}
void EndProcessRequest(IAsyncResult result)
{
Nito.AsyncEx.AsyncFactory.ToEnd(result);
}
void ProcessRequest(HttpContext context)
{
EndProcessRequest(BeginProcessRequest(context, null, null));
}
public virtual bool IsReusable
{
get { return true; }
}
}
// Now, our (async) Task implementation
public class MyAsyncHandler : HttpAsyncHandlerBase
{
public override async Task ProcessRequestAsync(HttpContext context)
{
using (var webClient = new WebClient())
{
var data = await webClient.DownloadDataTaskAsync("http://my resource");
context.Response.ContentType = "text/xml";
context.Response.OutputStream.Write(data, 0, data.Length);
}
}
}
(As noted in the code, .NET 4.5 has a HttpTaskAsyncHandler
which is similar to our HttpAsyncHandlerBase
above).
The really cool thing about async
is that it doesn't take any threads while doing the background operation:
- An ASP.NET request thread kicks off the request, and it starts downloading using the
WebClient
.
- While the download is going, the
await
actually returns out of the async
method, leaving the request thread. That request thread is returned back to the thread pool - leaving 0 (zero) threads servicing this request.
- When the download completes, the
async
method is resumed on a request thread. That request thread is briefly used just to write the actual response.
This is the optimal threading solution (since a request thread is required to write the response).
The original example also uses threads optimally - as far as the threading goes, it's the same as the async
-based code. But IMO the async
code is easier to read.
If you want to know more about async
, I have an intro post on my blog.