Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Processing Long Running Tasks With Asynchronous Handlers and XMLHTTP

0.00/5 (No votes)
17 May 2005 2  
This article shows you how to use async handlers along with XMLHTTP to process long executing tasks in ASP.NET.

Sample Image - main.jpg

Introduction

Processing long running tasks (i.e., SQL query execution, Web Services calls, etc.) has always been an important feature for high demand web applications. One problem these applications face is that of keeping the client UI responsive as the task executes.

Many sites, like Expedia, Travelocity etc. use intermediate 'Please Wait...' pages to work around the problem. Other sites, like Google Maps use client-side features such as XMLHTTP to make such requests without moving the user from the current page. The purpose of this article is to show how to use XMLHTTP along with ASP.NET to make the end user experience a bit more tolerable.

Background

Architecturally speaking, a key feature of ASP.NET is its objects have a short lifespan. This is extremely important to high demand web sites because it allows the application to execute quickly and efficiently and still be able to maintain a high level of concurrent users. The key factor behind keeping an ASP.NET web site responsive is to be aware of the framework constructs that control page execution.

A lot of developers know that to create a simple web page in ASP.NET one must use the System.Web.UI.Page class. However, some of them might not be aware that there are other ways to handle requests made to the application. This is where System.Web.IHttpHandler and System.Web.IHttpAsyncHandler interfaces come into play.

IHttpHandler and IHttpAsyncHandler: A Quick Look

All request execution in ASP.NET happens through the System.Web.IHttpHandler interface. If you look at the object diagram of the System.Web.UI.Page class, you can see that it inherits from the System.Web.UI.TemplateControl and implements the System.Web.IHttpHandler interface. The class definition and object hierarchy looks like this:

/*
Class Definition for System.Web.UI.Page
*/
public class Page : TemplateControl, IHttpHandler

System.Web.UI.Page hierarchy

The IHttpAsyncHandler interface inherits from IHttpHandler and defines two extra methods, BeginProcessRequest and EndProcessRequest respectively. These methods make up the mechanism needed to handle asynchronous calls within ASP.NET.

For this type of scenario, it is better to use the IHttpAsyncHandler rather than a Page object because of all the extra work (child control rendering, event handler notification, etc.) the Page performs per request. For more information on IHttpAsyncHandler, please see the MSDN documentation.

A Poor Man's Guide to Threading in ASP.NET

There are three simple ways you can accomplish threading within your ASP.NET application:

  1. Using the System.Threading.ThreadPool.
  2. Using a custom delegate and calling its BeginInvoke method.
  3. Using custom threads with the aid of System.Threading.Thread class.

The first two methods offer a quick way to fire off worker threads for your application. But unfortunately, they hurt the overall performance of your application since they consume threads from the same pool used by ASP.NET to handle HTTP requests. In other words, if you invoke five pool/delegate threads within a request, your application will only have 20 available threads remaining in its pool (by default, ASP.NET gives every application 25 worker threads). The way to get around this problem is to use the third option since the threads created are not part of your application's pool.

If you would like to read more on threading in ASP.NET, please see the References section.

Stepping Through the Code

The example used with this article is that of a simple weather service. The UI allows the user to enter US Zip codes to check current weather condition of cities. The reason why I chose a web service call is because it gives a great 'real world' example on how to use the asynchronous capabilities of ASP.NET and XMLHTTP.

When a user selects an entered zipcode from the list and hits the Check Weather button, two async requests are created, one to the weather checker handler and the other to the web service. A DHTML progress bar is used to keep the user 'entertained' while this process executes. Once a response is received by the client, the progress bar is stopped and the UI updated.

The main class behind this example is the AsyncTaskHandler class. This class is mapped to an .ashx file so ASP.NET can hook up the request chain. This class implements two interfaces the IHttpAsyncHandler, mentioned earlier, and the System.Web.SessionState.IRequiresSessionsState interface that is used to tell ASP.NET that this handler has access to session state. The class then uses two classes AsyncRequestResult and AsyncRequest to process the request.

AsyncRequestResult implements the System.IAsyncResult interface so ASP.NET can call the appropriate methods necessary to perform a call back. The AsyncRequest class is used to house a call to an external weather Web Service and write back the response to the waiting client. The collaboration between the objects can be seen by the following diagram:

Async request object collaboration

  1. As soon as the handler receives a request, it creates an object of type AsyncRequestResult.
  2. Using this object as a constructor parameter, the handler then creates an object of type AsyncRequest.
  3. Finally, the handler creates a worker thread of type System.Thread.Thread and uses the Process method of AsyncRequest and returns the created AsyncRequestResult back to ASP.NET and a response to the client.

Most of the core logic takes place within the AsyncRequest.Process method. Within this method, the associated AsyncRequestResult object is used to make a call to the weather information web service and to return an HTML snippet back to the client. The method looks as follows:

/// <summary>

/// Uses the IAsyncResult object to call a web service and writes

/// back the response to the caller

/// </summary>

public void Process()
{
    try
    {
        // Get the zip code from the AsynRequestResult object

        string strZip = Result.AsyncState as string;
        int zipCode = Int32.Parse(strZip);

        // Get the weather information

        string message = Result.GetWeather(zipCode);

        // Write back to the client

        Result.Context.Response.Write(message);
    }
    finally
    {
        // Tell ASP.NET that the request is complete

        Result.Complete();
    }
}

The actual call to the weather web service under the AsyncRequestResult.GetWeather method looks like:

///<summary>

/// Gets the current weather information based on a US Zip code

/// </summary>

/// <param name="zipCode">City zip code</param>

/// <returns>Location name along with current temperature.

///               Empty string otherwise.</returns>

public string GetWeather(int zipCode)
{
    string message = "";

    try
    {
        // Call the web service

        ExtendedWeatherInfo info = 
            weatherService.GetExtendedWeatherInfo(zipCode);

        // Format the message

        message = string.Format("<h2>{0} - {1}" + 
           "</h2>Current Temperature: {2}a<br>Feels Like: {3}.", 
           zipCode.ToString(), info.Info.Location, 
           info.Info.Temprature, info.Info.FeelsLike);
    }
    catch
    {
        message = "An error was encountered while calling web service.";
    }
    
    return message;
}

Now that you have a quick overview of how the code works, let's see how the client creates the request and handles the response.

Using XMLHTTP Within IE

To make an HTTP request programmatically through JavaScript, you need to use XMLHTTP. For quite some time, Microsoft had bundled a version of this technology within their MSXML parser. To create a JavaScript object that binds to this feature, you do the following:

var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");

This function will make a full asynchronous request to a URL and attach a callback function:

/*
    Creates a XMLHTTP async-call to an ASP.NET async handler
*/
function postRequest(url)
{
    var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    
    // 'true' specifies that it's a async call

    xmlhttp.Open("POST", url, true);
    
    // Register a callback for the call

    xmlhttp.onreadystatechange = 
        function ()
        {
            if (xmlhttp.readyState == 4)
            {
                var response = xmlhttp.responseText;
                divResponse.innerHTML += "<p>" + response + "</p>";
                
                stopProgressBar();
            }
        }
    
    // Send the actual request

    xmlhttp.Send();
}

From here, while we wait for the async handler to execute the long processing request, we show the users a simple little progress bar showing them that their request is being processed.

Making the UI responsive while executing a async call

The following diagram shows how the execution of the request from XMLHTTP takes place:

Full request to handler and UI response

  1. An XMLHTTP object is created when the user clicks on the Check Weather button.
  2. This object creates an async (non-blocking) request to weatherchecker.ashx.
  3. A temp callback is attached to the object to handle the response from the handler.
  4. Another JavaScript function is called to start the progress bar and display the 'Processing request...' text.
  5. Whenever the handler is finished executing the call to the web service, a response is generated back to the client's temp callback.
  6. The temp callback is executed in the client, the response text is set to a <div>, and the progress bar is stopped.

The final result from this process looks like:

Result from call

Conclusion

Having the ability of executing long running tasks asynchronously is a huge benefit for any web application regardless of size. I hope that this article shows developers an alternate way of tackling this design problem. Also, I hope that developers are more conscious when making decisions to add threading to their ASP.NET applications.

Please feel free to make any comments or suggestions to this article by using the forums below.

Reference

During my writing of this article, I used the following resources:

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here