Click here to Skip to main content
Click here to Skip to main content

Building a Better Wait Page

, 17 Mar 2005
Rate this:
Please Sign up or sign in to vote.
Building a better wait page using asynchronous programming and the XmlHttpRequest object.

Introduction

Any one who has ever written a web application that needed to perform some long running task has probably implemented a 'wait' page. Since web users are notoriously impatient, the wait page gives the user some visual feedback that something is indeed actually happening and assures them that if they just sit back, relax, and wait patiently, the next page really is on the way.

The Problem

To perform your long running process and display the friendly wait page to the user requires two things to be done at once. Unfortunately, most web programming techniques are not well suited to multi-tasking in this way. Add to the equation the fact that the Internet is a client-server architecture, where the server only responds when the client makes a request. That means that the server cannot 'tell' the browser, when it is done with a long process after the client has already received the wait page. In classic ASP and other web programming languages, there were few options to address either issue.

One frequently-implemented option was to redirect from your input page to a wait page, and then almost immediately redirect to another page that would actually do the processing. This approach took advantage of a browser 'feature' that didn't allow the browser to render the next page until it was completely finished processing. In the meantime, the previous page stayed visible. This approach had two main drawbacks: if your wait page contained any animated GIFs (which wait pages are famous for), they would stop animating as soon as the long running page was requested; and secondly: the browser gave the impression that it was 'doing something' (throbber was spinning), but nothing was happening. Impatient users quickly learned to hit 'Stop' or 'Back' and try the process again, causing even more problems and obviously not very desirable.

Another solution was to have the wait page refresh continuously every few seconds, checking to see if the long running process had completed. This required the process to set a flag in the database or some other signal, and the user experience of watching the wait page reload over and over was less than desirable.

The Solution

With the advent of ASP.NET, web programmers could now take advantage of an asynchronous programming model. So now the problem of 'doing two things at once' was solved. But what about the client-server architecture that prevented the server 'calling back' to the client? Well, a handy-dandy little tool called XmlHttpRequest is the answer. (You might have seen this little gem recently starring in Google Suggest and making some big waves.) By combining an asynchronous method call and background XmlHttpRequests, your wait page suddenly gets a lot smarter.

Using the code

There are four main components that make up this solution, each outlined below:

  • Input page - responsible for firing off the asynchronous event
  • Wait page - responsible for checking the status of the process and entertaining the user
  • CheckStatus page - simply indicates if the process has completed yet or not
  • Confirmation page - displayed after the process has completed

Input page

This is the page that the user uses to kick off the long-running event. In the page's code-behind, the following code fires off the process using .NET's asynch programming model. Also note that the resulting IAsyncResult is saved into the Session so we can query it later on.

string confirmationNumber;

private void Button_Click()
{
    IAsyncResult ar = DoSomethingAsync("abc");
    Session["result"] = ar;
    Response.Redirect("wait.aspx");
}

private IAsyncResult DoSomethingAsync(string someParameter)
{
    DoSomethingDelegate doSomethingDelegate = 
                                           new DoSomethingDelegate(DoSomething);
    IAsyncResult ar = doSomethingDelegate.BeginInvoke(someParameter, 
        ref confirmationNumber, new AsyncCallback(MyCallback), null);
    return ar;
}

private delegate bool DoSomethingDelegate(string someParameter, 
                                        ref string confirmationNumber);

private void MyCallback(IAsyncResult ar) 
{
    AsyncResult aResult = (AsyncResult) ar;
    DoSomethingDelegate doSomethingDelegate = 
                            (DoSomethingDelegate) aResult.AsyncDelegate;
    doSomethingDelegate.EndInvoke(ref confirmationNumber, ar);
}

private void DoSomething(string someParameter, ref string confirmationNumber)
{
    Thread.Sleep(10000);    //simulate a long process by waiting for ten seconds
    confirmationNumber = "DONE!";
    Session["confirmationNumber"] = confirmationNumber;
}

Wait Page

The wait page doesn't need any code-behind code because it is simply a display page that uses JavaScript and XmlHttpRequest to poll the server to see when the long-running process is done. Essentially, the page makes a client-side request of the CheckStatus.aspx page and then acts according to the response. All of this happens without refreshing the page. It is also important to note that this technique is cross-browser compatible, working in IE5+, Mozilla, Netscape 6+, Firefox 1+, and others. Note that as of this writing, the Opera browser does not support the XmlHttpRequest object, but adventurous programmers could emulate this functionality using a hidden IFrame as Google Suggest does. That is left as an exercise for the programmer =).

<script language="javascript">
<!--
var pollInterval = 1000;
var nextPageUrl = "confirmation.aspx";
var checkStatusUrl = "checkStatus.aspx";
var req;

// this tells the wait page to check the status every so often
window.setInterval("checkStatus()", pollInterval);

function checkStatus()
{
    createRequester();

    if(req != null)
    {
        req.onreadystatechange = process;
        req.open("GET", checkStatusUrl, true);
        req.send(null);
    }
}

function process()
{
    if(req.readyState == 4) 
    {
        // only if "OK"
        if (req.status == 200) 
        {
            if(req.responseText == "1")
            {
                // a "1" means it is done, so here is where you redirect
                // to the confirmation page
                document.location.replace(nextPageUrl);
            }
            // NOTE: any status other than 200 or any response other than
            // "1" require no action
        }
    }
}

/*
Note that this tries several methods of creating the XmlHttpRequest object,
depending on the browser in use. Also note that as of this writing, the
Opera browser does not support the XmlHttpRequest.
*/
function createRequester()
{
    try
    {
        req = new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch(e)
    {
        try
        {
            req = new ActiveXObject("Microsoft.XMLHTTP");
        }
        catch(oc)
        {
            req = null;
        }
    }

    if(!req && typeof XMLHttpRequest != "undefined")
    {
        req = new XMLHttpRequest();
    }
    
    return req;
}
//-->
</script>

Also note the use of document.location.replace(); when doing the redirect to the confirmation page. This effectively removes the wait page from the browser's history so that the user does not hit the 'Back' button and start the process over inadvertently.

CheckStatus page

This is really a very simple page. It just checks the IAsyncResult to see if it is completed, and returns a bit (0 or 1) indicating that status.

private void Page_Load(object sender, System.EventArgs e)
{
    AsyncResult ar = (AsyncResult) Session["result"];
    if(ar.IsCompleted)
        Response.Write("1");
    else
        Response.Write("0");
    Response.End();
}

Confirmation Page

Once the long-running process has completed, the wait page will get a signal from the CheckStatus page that it is OK to proceed. Once that happens, the wait page will redirect to the confirmation page and display the results to the user - pretty regular stuff that you can implement as needed.

Points of Interest

With all of the interest in Google Suggest and the XmlHttpRequest object lately, I thought it would be fun to show how it can solve a real-world problem. Plus, I thought it was about time that the lowly wait page was given a little polish of its own.

History

  • 03.17.2005 - Initial version released.

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

Share

About the Author

brian dunnington
Web Developer
United States United States
No Biography provided

Comments and Discussions

 
GeneralMy vote of 1 PinmemberArun Kailas A P3-Feb-14 19:55 
GeneralFixes Summary and Thanks PinmemberAndrew Weddle9-Jan-09 0:01 
GeneralWait never ends.. PinmemberPreet909011-Sep-08 4:36 
GeneralNeed suggessions Pinmemberneelimakrishna18-Jun-08 1:32 
GeneralRedirecting to an error page when exception occurs in DoSomething() Pinmemberkkcomm21-Apr-08 5:37 
GeneralImage PinmemberMember 17201833-Mar-08 2:38 
GeneralAdding Querystring to Next Page URL variable Pinmemberirfansk25-Oct-07 0:57 
GeneralMany thanks PinmemberDigitalRick9-Sep-07 18:17 
QuestionSession state not working Pinmemberharlee24-Aug-07 21:12 
GeneralRe: Session state not working PinmemberMember 337404324-Jan-08 7:18 
GeneralSuper PinmemberFatihSahin2-Feb-07 4:28 
GeneralRe: Super Pinmemberr_maiya9-Mar-07 8:57 
QuestionCan I get information back about what's going on... Pinmembertravich5-Jan-07 6:54 
QuestionThank you - is this possible? PinmemberSaavik16-Nov-06 9:40 
GeneralI get an Stack Overflow at line: 0 error Pinmembermaximus77-Mar-06 3:02 
QuestionWhat's the purpose of the Confirmation Number? Pinmemberdwfresh12-Feb-06 18:20 
Generalis this possible... Pinmemberrodchar29-Nov-05 12:10 
AnswerRe: is this possible... PinmemberSimone Busoli27-Dec-05 20:13 
GeneralCan't call Data / Business layers PinmemberChaci16-Nov-05 5:39 
GeneralRe: Can't call Data / Business layers PinmemberChaci18-Nov-05 2:22 
GeneralRe: Can't call Data / Business layers PinmemberAjrob36018-Dec-12 5:35 
QuestionTransferring to same page question... Pinmemberike2010201011-Oct-05 6:35 
GeneralStop Process on error Pinmemberjmlsgateway24-Aug-05 7:52 
GeneralRe: Stop Process on error PinmemberGabe Wishnie2-Oct-05 11:29 
GeneralWaitpage doen not work with SessionState mode PinsussRaman SAngra21-Jul-05 20:34 
GeneralRe: Waitpage doen not work with SessionState mode Pinmemberscottydgibson11-Oct-05 11:27 
GeneralRe: Waitpage doen not work with SessionState mode PinmemberStyjan3-Dec-05 15:08 
QuestionRe: Waitpage doen not work with SessionState mode Pinmemberharlee24-Aug-07 21:11 
GeneralQuestion about Wait Page Pinmemberdougloj15-Jun-05 0:45 
GeneralRe: Question about Wait Page Pinmemberjemodurn24-Jun-05 10:33 
General411 Length Required PinmemberRykie13-Jun-05 15:52 
GeneralCrystal Report Wait Page PinmemberPwbriggs26-May-05 9:02 
GeneralRe: Crystal Report Wait Page Pinmembermalakai2-Jun-05 6:59 
GeneralWaitpage does not wait PinmemberSabrina200417-May-05 7:09 
GeneralRe: Waitpage does not wait PinsussAnonymous20-May-05 12:41 
GeneralIt does not work Pinmembercodesheng15-May-05 15:54 
GeneralRe: It does not work Pinmemberjpierzchalski16-May-05 9:51 
GeneralRe: It does not work Pinmembercodesheng17-May-05 16:05 
GeneralRe: It does not work Pinmemberdougloj20-Aug-05 22:59 
GeneralRe: It does not work PinmemberJL. ROBERT2-May-07 23:48 
Generaljs bug Pinsussjf-aspnet10-May-05 14:44 
GeneralRe: js bug Pinmemberzeljko_v13-May-05 6:12 
GeneralDoSomething method wants two parametes... Pinmemberskyeoctober9-May-05 7:48 
GeneralRe: DoSomething method wants two parametes... Pinmemberbrian dunnington9-May-05 13:49 
GeneralRe: DoSomething method wants two parametes... PinsussAnonymous9-May-05 16:41 
GeneralRe: DoSomething method wants two parametes... Pinmemberbhorine10-May-05 4:45 
GeneralRe: DoSomething method wants two parametes... Pinmemberskyeoctober10-May-05 5:16 
GeneralSame in vb.net PinsussRykie8-May-05 19:43 
GeneralRe: Same in vb.net Pinmemberbrian dunnington9-May-05 13:51 
GeneralRe: Same in vb.net PinmemberRykie10-May-05 17:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.141015.1 | Last Updated 17 Mar 2005
Article Copyright 2005 by brian dunnington
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid