Samstag, April 22, 2006

Less waiting on AJAX

It's a psychological phenomena that waiting takes less time as long as something happens.

I'm sure you have seen all these nice rotating arrows, bouncing points or bars that are commonly used for situations where a progress bar should appear to tell the user that it's time for waiting like on the windows start up screen on http://www.pageflakes.com/ or even in windows media edition. It can also be as simple as the [Loading...] text that is used by the Google-Mail user interface.

So, when I fell over http://www.ajaxload.info last week where you can easily generate "AJAX-" animated gif files I thought it is time to implement a few lines into the AJAX Engine.

The right place to start displaying a progress indicator is just before starting the webservice call to the server. Here I call StartProgress().

But it also has to be hidden after anything happens that ends the action like when the return value is there, an exception happens or when the timeout timer strikes. To identify these places I searched the code for the ajax.current gets cleared. There I call EndProgress();

The first implementation was straight forward creating and removing a html element. After some testing I found that this costs me more time than the real call over the internet and in many situations immediate responses got slower and that's definitively not that what I wanted to achieve.

In the end I came to the following solution:

  • The StartProgress function only sets a flag (ajax.progress) to true and starts a timer (ajax.progressTimer) with a timeout of 220 msec.
  • This time was chosen by some testing and many server calls do not last so long and therefore need no progress indicator.
  • When the timer strikes it calls the ajax.ShowProgress function. Here I implement the real code that creates the HTML element or just shows an existing one again.
  • The EndProgress function clears the flag and also starts the timer but with some less waiting.
  • When the timer strikes after a call has finished the existing object is just hidden.

This architecture has some advantages. First the progress indicator is not shown when short calls are made and when multiple calls are made one after the other it is not hidden. This can save a lot of flickering.

Here are the specific new functions:

// ----- show or hide a progress indicator -----

// show a progress indicator if it takes longer...
ajax.StartProgress = function() {
  ajax.progress = true;
  if (ajax.progressTimer != null)
    window.clearTimeout(ajax.progressTimer);
  ajax.progressTimer = window.setTimeout(ajax.ShowProgress, 220);
} // ajax.StartProgress


// hide any progress indicator soon.
ajax.EndProgress = function () {
  ajax.progress = false;
  if (ajax.progressTimer != null)
    window.clearTimeout(ajax.progressTimer);
  ajax.progressTimer = window.setTimeout(ajax.ShowProgress, 20);
} // ajax.EndProgress


// this function is called by a timer to show or hide a progress indicator
ajax.ShowProgress = function() {
  ajax.progressTimer = null;
  var a = document.getElementById("AjaxProgressIndicator");
 
  if (ajax.progress && (a != null)) {
    // just display the existing object
    a.style.top = document.documentElement.scrollTop + 2 + "px";
    a.style.display = "";
   
  } else if (ajax.progress) {
    // find a relative link to the ajaxcore folder containing ajax.js
    var path = "../ajaxcore/"
    for (var n in document.scripts) {
      s = document.scripts[n].src;
      if ((s != null) && (s.length >= 7) && (s.substr(s.length -7).toLowerCase() == "ajax.js"))
        path = s.substr(0,s.length -7);
    } // for
   
    // create new standard progress object
    a = document.createElement("div");
    a.id = "AjaxProgressIndicator";
    a.style.position = "absolute";
    a.style.right = "2px";
    a.style.top = document.documentElement.scrollTop + 2 + "px";
    a.style.width = "98px";
    a.style.height = "16px"
    a.style.padding = "2px";
    a.style.verticalAlign = "bottom";
    a.style.backgroundColor="#51c77d";

    a.innerHTML = "<img style='VERTICAL-ALIGN:bottom' src='" + path + "ajax-loader.gif'> please wait...";
    document.body.appendChild(a);

  } else if (a) {
    a.style.display = "none";
  } // if
} // ajax.ShowProgress

You can find the full source here and I will also include it into the next ajax.zip.

If you want to see how it looks like you can use the old prime factor sample. Try some long the numbers like: 98798798789878987. You might see it only if someone else is stressing the server too - It seems to be a powerful machine :-) and prime factors get calculated fast even with my stupid algorithm :-).

1 Kommentar:

Martin Edelius hat gesagt…

Thanks for the information -- I learned a lot from it. :)