Interface Jquery ajax queries and timeouts — get the timing right

So you’ve got a nice web UI that interfaces with something else on your server – a database is a pretty common example. You’re using jQuery, so you set yourself up an ajax call to get your data, something simple like:

function wsgiCallbackTableData (database,table,callback,callbackoptions) {
    callbackoptions=callbackoptions || {}
    $.ajax({
        url: "/wsgisqlitequery",
        type: "post",
        datatype:"json",                
        data: {'database':database,'table':table},
        success: function(response){
            // Execute our callback function
           callback(response,callbackoptions);  
        }
    });    
}

This means you have wsgi configured on your server and a script aliased to ‘/wgisqlitequery’ that interprets the object you’ve passed to it as a command to grab the specified table from the specified database, in this case an sqlite database. Send in some options in callbackoptions and send them on to your callback function. Pretty vanilla stuff, as described here.

The tricky part is the timing. Because this call is asynchronous, a setInterval() or setTimeout() will not take into account the time it takes to execute and process the data. At best, you end up with erratic and unpredictable intervals depending on client and server-side load. At worst, you can end up stacking functions indefinitely and crashing your users browsers and bogging down your server. For example, if you set an interval of 500ms and your queries are taking 2s, you will just continue to stack concurrent requests. As simultaneous queries stack, each slows down, and they stack indefinitely until the browser gives up. No bueno.

If you set an interval of 500ms and your queries are taking 2s, you will just continue to stack concurrent requests until the browser gives up.

The key thing is remembering that the ajax call is asynchronous. This is a good thing — you don’t want your slow query to block execution of the remainder of the page. We send the data request into the sunset, and when it’s done we get the answers we were searching for. What we want is for the next timeout to begin when the asynchronous function ends.

Here’s how we do it. The concept is common and I personally learned it here (a very good read by Paul Irish on jquery). We set up a self-executing function in the callback. For synchronous calls, it would look something like this:

(function myFun(){
    doStuff();
    setTimeout(function(){myFun(),timeout})
})()

We run doStuff(), then reexecute the function only once the stuff is done. If we set it up as self-executing as we have here, it runs immediately and indefinitely at the timeout period specified. Let’s add two things. First, the option to run not on timeout:

function myFun(myoptions){
    doStuff();
    myoptions=myoptions || {}
    if (myoptions.timeout > 0){
        setTimeout(function(){myFun(),myoptions.timeout})
    }
}

Now we can run the function with or without timeout. This is useful if you need an updater running in the background, but also need to call it on, say, an updating button click.

Next we add the ajax. This is pretty simple. We just put the invocation of the function in the callback. This way it doesn’t execute until data (or error) is returned. Most important, it will not execute again until it has successfully completed. Dead query? No problem. Fail once. Something to keep in mind, however, if you know you’ll only succeed some of the time.

function updateStuff(myoptions){
    myoptions=myoptions || {}
    wsgiCallbackTableData ('/my/dbpath','mytable',myCallbackFunction,myoptions)
} 

function myCallbackFunction(response,myoptions){
    doStuff(response);
    if (myoptions.timeout > 0){
        setTimeout(function()
        {updateStuff(),myoptions.timeout})
    }
}

Now, if you really need to nail down timing, you could quite easily determine how each query takes, pass the value in the callbackoptions and adjust your timeouts in real-time to predictively attempt regularity, Ultimately, however, with the asynchronous call it’s just going to take how long it’s going to take.

Another neat thing you can do using this basic method is to allow updates of your timeouts. Sounds unimportant unless you need to do it! What if a user wants to set the update time of the interface on the fly without a refresh? We can do that. Just grab your timeout value in each self-executing function, for example:

myoptions.timeout=someGlobalVariable

OR

myoptions.timeout=$('#someControlWidget').val()

Then carry on. See here for a simple example. All the meat is here:

<script>
  $(document).ready(function(){
    (function ticktock(){
      console.log($('#ticker').val())
      if ($('#ticker').val()=='tick') {
          $('#ticker').val('tock')
      }
      else {
          $('#ticker').val('tick')
      }
      $('#ticker').slider('refresh')
      console.log('running at ' + $('#timeoutslider').val() + ' second intervals')
      setTimeout(function(){ticktock()},$('#timeoutslider').val()*1000) 
    })()
  })
</script>

Until next time.
~C

Leave a Reply

Your email address will not be published. Required fields are marked *