Progress Bar Using AJAX: Design Details

80酷酷网    80kuku.com

  ajax

Progress Bar Using AJAX: Design Details

The sequence diagram below details the participants on both the client and server needed to provide an AJAX progress bar. The details are explained in further detail in the remainder of this document.

Tracking the progress of a server-side operation by a client requires two operations which include:

  1. Negotiate a Task Id: This Id is used by the polling loop to track the progress of the task.
  2. Polling Loop: The polling loop will continue until the task is complete.

Now let us review each of these operations in more detail.

Negotiate a Task Id

An HTML form button onClick event calls a JavaScript function which in turn will create an XMLHttpRequest object and the URL and callback function as may be seen in the JavaScript code snippet below.

function submitTask() {
var url = "task?action=startTask";
var bttn = window.document.getElementById("taskbutton");
bttn.disabled = true;
initRequest(url);
// set callback function
req.onreadystatechange = processInitialRequest;
req.send(null);
}

function initRequest(url) {
if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
} else if (window.ActiveXObject) {
isIE = true;
req = new ActiveXObject("Microsoft.XMLHTTP");
}
req.open("GET", url, true);
}

The url in the code above includes a URL parameter action which is set to "startTask." When the req.send(null); statement is executed the request is sent to the URL "task" The client continues processing page events because the interaction is configured to be an asynchronous HTTP GET with the req.open("GET", url, true); statement.

On the server, a servlet TaskServlet is mapped to the URL "task." When the TaskServlet receive a request containing a parameter "action" with the value of "startTask," a new Task is created as can be seen in the code snippet below.

            if ((action != null) && action.equals("startTask")) {
// this task will take 15 cycles of the counter to complete
Task t = new Task(counter, 15);
taskCounter++;
tasks.put(taskCounter + "", t);
// return intial content
response.setContentType("text/xml");
response.setHeader("Cache-Control", "no-cache");
PrintWriter pw = response.getWriter();
pw.write("<message&qt;" + taskCounter + "</message>");
pw.flush();
}

The snippet of code from the TaskServlet above shows the TaskServlet creating a new Task object and placing it in a java.util.HashMap with a unique identifier. For this example, an int taskCounter is used to identify each Task.

The taskCounter increments each time a new Task is created. In cases where the progress will be tracked in an environment outside the firewall, more unique identifiers may be necessary to prevent nefarious user from tracking operations they did not initiate.

The taskCounter is the unique id that is returned to the XMLHttpRequest in an XML document. This id is later used to track the progress of the Task when the client polls the TaskServlet. In this example, the TaskServlet has an internal timer running on a thread which sleeps for 2 seconds and increments the variable counter. To simulate a server-side operation this example sets the Task duration be 15 increments of the counter (30 seconds total).

On the client, the response from the TaskServlet is received by the XMLHttpRequest object which calls the processIntialRequest() callback function shown below.

// callback function for intial request to schedule a task
function processInitialRequest() {
if (req.readyState == 4) {
if (req.status == 200) {
var item = req.responseXML.getElementsByTagName("message")[0];
var message = item.firstChild.nodeValue;
// the initial requests gets the targetId
targetId = message;
messageHash = 0;
window.status = "";
createProgressBar();
showProgress(0);
}
var idiv = window.document.getElementById("task_id");
idiv.innerHTML = "Task ID=" + targetId;
// do the initial poll in 2 seconds
setTimeout("pollTaskmaster()", 2000);
}
}

This processInitialRequest() function extracts the task id from the XML document and assigns it to the variable targetId. The function will then initialize the progress bar in the HTML DOM and and show the initial progress of 0. The statement setTimeout("pollTaskmaster()", 2000); is then executed to start the polling loop.

Polling Loop

The polling loop polls the TaskServet using the task id store in the targetId variable until the server-side operation is completed (a response of 100 is returned).

function pollTaskmaster() {
var url = "task?messageHash=" + escape(messageHash) + "&targetId=" + targetId;
initRequest(url);
req.onreadystatechange = processPollRequest;
req.send(null);
}

The pollTaskmaster() function configures the XMLHttRequest object with a URL and callback function. The request is sent to the URL "task" which is mapped on the server to the TaskServlet. The following code in the TaskServlet is responsible for processing the request.

                int percentage = 0;
if (tasks.get(targetId) != null) {
percentage = ((Task)tasks.get(targetId)).getPrecentComplete(counter);
}
if ((messageHash !=null) &&
(Integer.valueOf(messageHash).intValue()) == percentage) {
// nothing has changed
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
} else {
response.setContentType("text/xml");
response.setHeader("Cache-Control", "no-cache");
PrintWriter pw = response.getWriter();
if (percentage &qt; 100) percentage = 100;
pw.write("" + percentage + "");
pw.flush();
}

In the code above, the Task object is looked up using the unique id (targetId) provided in the request. The Task getPrecentComplete method is called with the current counter to calculate the progress of the operation and the results are returned in an XML document. Note that if the messageHash representing the current percentage of completion being displayed by the client matches the percentage complete of the operation in the Task (indicating no change) the TaskServlet will return an HTTP response code 304 (No Content). This saves time for post processing of the message by the client, time for message generation by the TaskServlet, and bandwidth for transmitting the message.

Upon receiving the response from the TaskServlet the XMLHttpRequest object will call the processPollRequest() to do post processing of the request.

function processPollRequest() {
if (req.readyState == 4) {
if (req.status == 200) {
var item = req.responseXML.getElementsByTagName("message")[0];
var message = item.firstChild.nodeValue;
showProgress(message);
messageHash = message;
} else {
window.status = "No Update for " + targetId;
}
if (messageHash < 100) {
setTimeout("pollTaskmaster()", 5000);
} else {
setTimeout("complete()", 2500);
}
}
}

The HTML DOM is updated by showProgress() function if the req.status HTTP status code is 200. This function will detect if the percentage from the server is complete (100) and stop the polling loop by calling the complete() function. Otherwise, the polling will continue to loop by executing the setTimeout("pollTaskmaster()", 5000); statement following a 5 second delay.

<

分享到
  • 微信分享
  • 新浪微博
  • QQ好友
  • QQ空间
点击: