|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionAny 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 ProblemTo 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 SolutionWith 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 Using the codeThere are four main components that make up this solution, each outlined below:
Input pageThis 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 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 PageThe wait page doesn't need any code-behind code because it is simply a display page that uses JavaScript and <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 CheckStatus pageThis is really a very simple page. It just checks the 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 PageOnce 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 InterestWith all of the interest in Google Suggest and the History
|
||||||||||||||||||||||