Click here to Skip to main content
12,998,785 members (68,191 online)
Click here to Skip to main content
Add your own
alternative version


57 bookmarked
Posted 11 Oct 2006

Another Simple Wait Page

, 11 Oct 2006
Rate this:
Please Sign up or sign in to vote.
An article to demonstrate to use asynchronous delegate call and client script callback to build long wait page

Sample Image - screenshot.jpg


Sometime you need to process a lengthy job in your web form. It is a better idea to build a wait page instead of letting your user just stare at the screen doing nothing.


There are a lot of solutions over Internet to accomplish this purpose. My solution isn't new, the initial idea is from Brian Dunnington's article Building a Better Wait Page in code project. I have been using this approach for long time in ASP.NET 1.1. Since the release of ASP.NET 2.0, Microsoft makes a lot easier to implement client side callback, I decided to expand this solution to include AJAX function, so that we don't need to refresh the page from time to time.

Using the code

There are 3 main areas you need to consider in this solution

  • Asynchronous delegate call
  • Hook up with client callback
  • Client script

Asynchronous delegate call

    /// <span class="code-SummaryComment"><summary>
</span>    /// Definition of delegate
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><param name="minute"></param>
</span>    /// <span class="code-SummaryComment"><returns></returns>
</span>    private delegate bool DoJobDelegate (int minute);

    /// <span class="code-SummaryComment"><summary>
</span>    /// To invoke the long process function
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><param name="minute"></param>
</span>    /// <span class="code-SummaryComment"><returns></returns>
</span>    private IAsyncResult DoJobAsync(int minute) {
        DoJobDelegate doDelegate = new DoJobDelegate(doLongJob);
        IAsyncResult ar = doDelegate.BeginInvoke(minute, 
                          new AsyncCallback(MyCallback), null);
        return ar;

    /// <span class="code-SummaryComment"><summary>
</span>    /// The server side callback handler
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><param name="ar"></param>
</span>    private void MyCallback (IAsyncResult ar) {
        AsyncResult aResult = (AsyncResult)ar;
        DoJobDelegate doDelegate = (DoJobDelegate)aResult.AsyncDelegate;
        // Session object is used to tell if process finishes or not
        Session["NewOrderResult"] = doDelegate.EndInvoke(ar);

    /// <span class="code-SummaryComment"><summary>
</span>    /// The main function to run long process
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><param name="minute"></param>
</span>    /// <span class="code-SummaryComment"><returns></returns>
</span>    private bool doLongJob (int minute) {
        System.Threading.Thread.Sleep(minute * 1000 * 60);
        if (minute % 2 == 0) {
            return true;
        } else {
            return false;

Hook up with client callback

In order to hook with client callback, you have to implement ICallbackEventHandler interface

public partial class Process : System.Web.UI.Page, ICallbackEventHandler {
 protected string CallBackEventReference;

Then we need to prepare script to reference client function

string ScriptRef = this.ClientScript.GetCallbackEventReference(

CallBackEventReference = ScriptRef;

This will create a client function like WebForm_DoCallback('__Page','CheckStatus',ClientCallBack,this,ClientCallBack,true);

From this point, you need to implement 2 functions of this interface

    /// <span class="code-SummaryComment"><summary>
</span>    /// Capture the event argument in this field, in this case I don't use it.
    /// <span class="code-SummaryComment"></summary>
</span>    string eventArgument = "";

    /// <span class="code-SummaryComment"><summary>
</span>    /// Returns result to client side
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><returns></returns>
</span>    string ICallbackEventHandler.GetCallbackResult() {
        if (Session["NewOrderResult"] != null) {
            // after async call finishes,
            // it sets this Session object to some value,
            // this method will capture the status
            if (Convert.ToBoolean(Session["NewOrderResult"])) {
                return "even.htm";
            } else {
                return "odd.htm";
        } else {
            return "";

    /// <span class="code-SummaryComment"><summary>
</span>    /// Gets parameter passed from client side
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><param name="eventArgument"></param>
</span>    void ICallbackEventHandler.RaiseCallbackEvent (string eventArgument) {
        this.eventArgument = eventArgument;

Client script

On the client side, we need to prepare some javascript functions with talk with server.

function ClientCallBack(Result, Context) {
    if (Result != "") {
        window.location.href = Result;

In order to make broswer to check server process status, we also need a timer to make client script to run at some certain interval.

<body onload="startClock();">
// check server every 5 seconds, adjust this value to your own preference
var interval = 5
var x = interval
function startClock(){
    x = x - 1;
    setTimeout("startClock()", 1000);
    if(x == 0){
       <%= this.CallBackEventReference %>

So what is happnening now? The client script will be triggered every 5 seconds to talk with server. And ICallbackEventHandler.GetCallbackResult() method on server side will be called every 5 seconds. In this method it checks Session object value, if asynchronous call finishes the process, it will call the MyCallback to set Session object to a not null value returned from process result to make ICallbackEventHandler.GetCallbackResult() be able to capture the result.

Points of Interest

My first explore of client callback, I hope to add more features later.


  • 2006-10-11 Initial version.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Hardy Wang
Canada Canada
No Biography provided

You may also be interested in...

Comments and Discussions

GeneralIs it possible to use this in a wizard Pin
D.Sridhar5-May-08 8:26
memberD.Sridhar5-May-08 8:26 
GeneralRe: Is it possible to use this in a wizard Pin
Hardy Wang5-May-08 8:37
memberHardy Wang5-May-08 8:37 
Generalthe job is load a new page Pin
jrmora7-Mar-08 5:02
memberjrmora7-Mar-08 5:02 
GeneralRe: the job is load a new page Pin
Hardy7-Mar-08 6:07
memberHardy7-Mar-08 6:07 
QuestionSQL Server Session State Pin
Simon Byrne16-May-07 4:37
memberSimon Byrne16-May-07 4:37 
AnswerRe: SQL Server Session State Pin
Hardy4-Jun-07 3:38
memberHardy4-Jun-07 3:38 
Generalbrowser back button Pin
Joaquin Luna9-Dec-06 10:36
memberJoaquin Luna9-Dec-06 10:36 Pin
Papichulo.NET18-Oct-06 11:04
memberPapichulo.NET18-Oct-06 11:04 
GeneralRe: Pin
Christian Graus18-Oct-06 11:39
staffChristian Graus18-Oct-06 11:39 
GeneralRe: Pin
Hardy19-Oct-06 5:15
memberHardy19-Oct-06 5:15 
AnswerRe: Pin
Joaquin Luna8-Dec-06 11:33
memberJoaquin Luna8-Dec-06 11:33 
Questionempty source files? Pin
alex_-_12-Oct-06 4:42
memberalex_-_12-Oct-06 4:42 
AnswerRe: empty source files? Pin
Hardy12-Oct-06 4:46
memberHardy12-Oct-06 4:46 
GeneralThanks ! Pin
dapoussin12-Oct-06 2:19
memberdapoussin12-Oct-06 2:19 
Your script works like a charm Smile | :)
Anyway, I've modified it just a bit so that all the JS code is generated by my code behind.
Here is a sample :

<br />
StringBuilder sb = new StringBuilder();<br />
sb.Append("function startClock(){\n");<br />
sb.Append("x = x - 1;\n");<br />
sb.Append("setTimeout(\"startClock()\", 1000);\n");<br />
sb.Append("if(x == 0){\n");<br />
sb.Append(this.ClientScript.GetCallbackEventReference(<br />
    this, "'CheckStatus'", "ClientCallBack", "this", "ClientCallBack", true) + ";\n");<br />
sb.Append("x = interval;\n");<br />
sb.Append("}\n");<br />
sb.Append("}\n\n");<br />
<br />
StringBuilder sb2 = new StringBuilder();<br />
sb2.Append("function ClientCallBack(Result, Context) {\n");<br />
sb2.Append("if (Result != \"\") {\n");<br />
sb2.Append("window.location.href = Result;\n");<br />
sb2.Append("}\n");<br />
sb2.Append("}\n");<br />
<br />
ClientScript.RegisterClientScriptBlock(this.GetType(), "startClock", sb.ToString(), true);<br />
ClientScript.RegisterClientScriptBlock(this.GetType(), "ClientCallBack", sb2.ToString(), true);<br />
ClientScript.RegisterStartupScript(this.GetType(), "startup", <br />
    "var interval = 2;\nvar x = interval;\nstartClock();", true);<br />


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

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

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170622.1 | Last Updated 11 Oct 2006
Article Copyright 2006 by Hardy Wang
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid