A Little Wrinkle in AJAX – Failed Requests

AJAX.jpg

I was working on a few problems this morning and ran into one that I thought I'd solved with a simple binary flag. Well... let's do the set-up.

We have a web page with a JavaScript timer on it, and every so often, it fires off a request to the server to get new data. It's a poor man's push notification system, but it works - expect when it doesn't. Case in point: what happens when the requests are taking longer to service at the server than the timer's interval? Well, they still get sent, and they stack up at the server, and slow things down even more.

It's a mess. We need to have some idea that the request is in process, and if so, we don't want to send anther to the server. It'd just make things worse. So my first cut looked a lot like this:

  var requesting = false;
 
  function refreshData() {
    if (!requesting) {
      requesting = true;
      ...
    }
  }
 
  function handleResponse() {
    ...
    requesting = false;
  }

And while it's not a good design, the idea is simple - have a global variable that indicates if there's a pending request, and if so, then just skip the current request as the timer will issue another in just a little bit. Should work great... and it does - except when it doesn't.

Where it fails is in the failure on the server. If the request fails, then the handleResponse() code might not be called. Some cases it will and some it won't - it depends on the library doing the call. In my case, that's the Google Visualization Query, and that seems to be about 50-50, depending on the server's condition.

When it fails, the flag will never get reset and from that point on, no requests will get sent to the server. That's not good. What I needed was a much more secure method of knowing when it's busy, with the ability to detect when it had failed.

What I came up with was the idea of a simple timeout. That, coupled with a global timeout limit should be able to do the trick:

  var requestTimeout = null;
 
  function refreshData() {
    var now = new Date();
    if ((requestTimeout == null) || (now > requestTimeout)) {
      requestTimeout = now;
      requestTimeout.setSeconds(requestTimeout.getSeconds() + ServletTimeout);
      ...
    }
  }
 
  function handleResponse() {
    ...
    requestTimeout = null;
  }

In this code sample, we're going to get the current date/time and compare the timeout to that value. If we've passed the timeout, or there is none, then we can hit it again - setting the new timeout to the appropriate distance in the future.

In my tests, this works great with servers that die on the page, and it also doesn't allow too many requests to buffer up on the server. Hopefully, there aren't any other gotchas that I missed with this guy.