3.2. Fallback Patterns

The previous section dealt with when to send or receive data from the server, which presupposes that everything goes according to plan on the server side: the request is received, the necessary changes are made, and the appropriate response is sent to the client. But what happens if there's an error on the server? Or worse yet, what if the request never makes it to the server? When developing Ajax applications, it is imperative that you plan ahead for these problems and describe how your application should work if one of these should occur.

3.2.1. Cancel Pending Requests

If an error occurs on the server, meaning that a status of something other than 200 or 304 is returned, you need to decide what to do. Chances are that if a file is not found (404) or an internal server error occurred (302), trying again in a few minutes isn't going to help, since both of these require an administrator to fix the problem. The simplest way to deal with this situation is to simply cancel all pending requests. You can set a flag somewhere in your code that says, "don't send any more requests." This clearly has the highest impact on solutions using the Periodic Refresh pattern.

The comment notification example can be modified to take this into account. This is a case where the Ajax solution provides additional value to the user but is not the primary focus of the page. If a request fails, there is no reason to alert the user; you can simply cancel any future requests to prevent any further errors from occurring. To do so, you must add a global variable that indicates whether requests are enabled:

var oXHR = null;
var iInterval = 1000;
var iLastCommentId = −1;
var divNotification = null;
var blnRequestsEnabled = true;

Now, the blnRequestsEnabled variable must be checked before any request is made. This can be accomplished by wrapping the body of the checkComments() function inside of an if statement:

function checkComments() {

    if (blnRequestsEnabled) {
        if (!oXHR) {
            oXHR = zXmlHttp.createRequest();
        } else if (oXHR.readyState != 0) {
            oXHR.abort();
        }

        oXHR.open("get", "CheckComments.php", true);
        oXHR.onreadystatechange = function () {
            if (oXHR.readyState == 4) {
                if (oXHR.status == 200 || oXHR.status == 304) {
                    var aData = oXHR.responseText.split("||");
                    if (aData[0] != iLastCommentId) {
                        iLastCommentId = aData[0];
                        if (iLastCommentId != −1) {
                            showNotification(aData[1], aData[2]);
                        }
                    }
                    setTimeout(checkComments, iInterval);
                }
            }
        };

        oXHR.send(null);
    }
}

But that isn't all that must be done; you must also detect the two different types of errors that may occur: server errors that give status codes and a failure to reach the server (either the server is down or the Internet connection has been lost).

To begin, wrap everything inside of the initial if statement inside a try...catch block. Different browsers react at different times when a server can't be reached, but they all throw errors. Wrapping the entire request block in a try...catch ensures that you catch any error that is thrown, at which point you can set blnRequestsEnabled to false. Next, for server errors, you can also set blnRequestsEnabled to false whenever the status is not equal to 200 or 304. This will have the same effect as if the server couldn't be reached:

function checkComments() {

    if (blnRequestsEnabled) {
        try {
            if (!oXHR) {
                oXHR = zXmlHttp.createRequest();
            } else if (oXHR.readyState != 0) {
                oXHR.abort();
            }

            oXHR.open("get", "CheckComments.php", true);
            oXHR.onreadystatechange = function () {
                if (oXHR.readyState == 4) {
                    if (oXHR.status == 200 || oXHR.status == 304) {

                        var aData = oXHR.responseText.split("||");
                        if (aData[0] != iLastCommentId) {

                            if (iLastCommentId != −1) {
                                showNotification(aData[1], aData[2]);
                            }

                            iLastCommentId = aData[0];
                        }

                        setTimeout(checkComments, iInterval);
                    } else {
                        blnRequestsEnabled = false;
                    }
                }
            };

            oXHR.send(null);
        } catch (oException) {
            blnRequestsEnabled = false;
        }
    }
}

Now, when either of the two error types occurs, an error will be thrown (either by the browser or by you), and the blnRequestsEnabled variable will be set to false, effectively canceling any further requests if checkComments() is called again.

NOTE

You may also have noticed that a timeout for another request is created only if the status is 200 or 304, which prevents another request from occurring for any other status. That works fine for server errors, but it doesn't do anything for communication errors. It's always better to have more than one way to handle errors when they occur.

3.2.2. Try Again

Another option when dealing with errors is to silently keep trying for either a specified amount of time or a particular number of tries. Once again, unless the Ajax functionality is key to the user's experience, there is no need to notify him or her about the failure. It is best to handle the problem behind the scenes until it can be resolved.

To illustrate the Try Again pattern, consider the Multi-Stage Download example. In that example, extra links were downloaded and displayed alongside the article. If an error occurred during the request, an error message would pop up in most browsers. The user would have no idea what the error was or what caused it, so why bother displaying a message at all? Instead, it would make much more sense to continue trying to download the information a few times before giving up.

To track the number of failed attempts, a global variable is necessary:

var iFailed = 0;

The iFailed variable starts at 0 and is incremented every time a request fails. So, if iFailed is ever greater than a specific number, you can just cancel the request because it is clearly not going to work. If, for example, you want to try 10 times before canceling all pending requests, you can do the following:

function downloadLinks() {
    var oXHR = zXmlHttp.createRequest();

    if (iFailed < 10) {
        try {
            oXHR.open("get", "AdditionalLinks.txt", true);
            oXHR.onreadystatechange = function () {
                if (oXHR.readyState == 4) {
                    if (oXHR.status == 200 || oXHR.status == 304) {
                        var divAdditionalLinks =
                                     document.getElementById("divAdditionalLinks");
                        divAdditionalLinks.innerHTML = oXHR.responseText;
                        divAdditionalLinks.style.display = "block";
                    } else {
                        iFailed++;
                        downloadLinks();
                    }
                }
            }

            oXHR.send(null);

} catch (oException) {
            iFailed++;
            downloadLinks();
        }
    }
}

This code is constructed similarly to the previous example. The try...catch block is used to catch any errors that may occur during the communication, and a custom error is thrown when the status isn't 200 or 304. The main difference is that when an error is caught, the iFailed variable is incremented and downloadLinks() is called again. As long as iFailed is less than 10 (meaning it's failed less than 10 times), another request will be fired off to attempt the download.

In general, the Try Again pattern should be used only when the request is intended to occur only once, as in a Multi-Stage Download. If you try to use this pattern with interval-driven requests, such as Periodic Refresh, you could end up with an ever-increasing number of open requests taking up memory.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset