Programming Microsoft ASP.NET 4 - Dino Esposito [108]
The Begin handler is responsible for starting any operation you fear can block the underlying thread for too long. The handler is expected to return an IAsyncResult object to describe the state of the asynchronous task. When the lengthy task has completed, the End handler finalizes the original request and updates the page’s user interface and controls. Note that you don’t necessarily have to create your own object that implements the IAsyncResult interface. In most cases, in fact, to start lengthy operations you just use built-in classes that already implement the asynchronous pattern and provide IAsyncResult ready-made objects.
The page progresses up to entering the PreRenderComplete stage. You have a pair of asynchronous event handlers defined here. The page executes the Begin event, starts the lengthy operation, and is then suspended until the operation terminates. When the work has been completed, the HTTP runtime processes the request again. This time, though, the request processing begins at a later stage than usual. In particular, it begins exactly where it left off—that is, from the PreRenderComplete stage. The End event executes, and the page finally completes the rest of its life cycle, including view-state storage, markup generation, and unloading.
Important
The Begin and End event handlers are called at different times and generally on different pooled threads. In between the two methods calls, the lengthy operation takes place. From the ASP.NET runtime perspective, the Begin and End events are similar to serving distinct requests for the same page. It’s as if an asynchronous request is split in two distinct steps: a Begin step and End step. Each request is always served by a pooled thread. Typically, the Begin and End steps are served by threads picked up from the ASP.NET thread pool. The lengthy operation, instead, is not managed by ASP.NET directly and doesn’t involve any of the pooled threads. The lengthy operation is typically served by a thread selected from the operating system completion thread pool.
The Significance of PreRenderComplete
So an asynchronous page executes up until the PreRenderComplete stage is reached and then blocks while waiting for the requested operation to complete asynchronously. When the operation is finally accomplished, the page execution resumes from the PreRenderComplete stage. A good question to ask would be the following: “Why PreRenderComplete?” What makes PreRenderComplete such a special event?
By design, in ASP.NET there’s a single unwind point for asynchronous operations (also familiarly known as the async point). This point is located between the PreRender and PreRenderComplete events. When the page receives the PreRender event, the async point hasn’t been reached yet. When the page receives PreRenderComplete, the async point has passed.
Building a Sample Asynchronous Page
Let’s roll a first asynchronous test page to download and process some RSS feeds. The page markup is quite simple indeed:
<%@ Page Async="true" Language="C#" AutoEventWireup="true"
CodeFile="TestAsync.aspx.cs" Inherits="TestAsync" %>
The code file is shown next, and it attempts to download the RSS feed from my personal blog:
public partial class TestAsync : System.Web.UI.Page
{
const String RSSFEED = "http://weblogs.asp.net/despos/rss.aspx";
private WebRequest req;
public String RssData { get; set; }
void Page_Load (Object sender, EventArgs e)
{
AddOnPreRenderCompleteAsync(BeginTask, EndTask);
}
IAsyncResult BeginTask(Object sender,
EventArgs e, AsyncCallback cb, Object state)
{
// Trace
Trace.Warn("Begin async: Thread=" +
Thread.CurrentThread.ManagedThreadId.ToString());
// Prepare to make a Web request for the RSS feed
req = WebRequest.Create(RSSFEED);
// Begin the operation and return an IAsyncResult object
return req.BeginGetResponse(cb, state);
}
void EndTask(IAsyncResult