Thursday 30 October 2014

Synchronizing the Asynchronous

AJAX is a very useful technique of combining client-side and server-side capabilities to form a coherent whole. It drives many front-end UI features we see today and greatly extends the traditional client-server relationship. But it's not without its flaws. Chief of which, the asynchronous nature of AJAX may confound the programmer if not properly handled. The following is an example of what I'm talking about.

Let's say you have an interface with three textboxes. You want to enter a value into the first textbox, and upon the click of a button, the value will appear in the second textbox. And then the second textbox's value will be multiplied by two and appear in the third textbox. Wait a second, I hear you exclaim. You don't need AJAX to do that! Yes, that is quite right. Simple JavaScript should suffice. And should take the average programmer less than a few minutes. However, I'm trying to illustrate a point about AJAX here as simply as possible, so bear with me.

Below should be your HTML front-end:

<!DOCTYPE html>
<html>
   <head>
      <title>AJAX test</title>
   </head>

   <body>
      <input type="text" id="txtNumber" value="0">
      <input type="button" value="Display">
      <br />
      <input type="text" id="txtDisplay" value="0" disabled="disabled"> Original
      <br />
      <input type="text" id="txtDisplayTimesTwo" value="0" disabled="disabled"> x2
   </body>
</html>

This is what your interface should look like:


Original
x2

Now for the purposes of this exercise, we're going to prepare two server-side scripts. The following examples are in PHP, but you may use any server-side language you're familiar with.

display.php
<?php
$number=$_GET["number"];
echo $number*2;
?>

displaytimestwo.php
<?php
$number=$_GET["number"];
echo $number*2;
?>

So far so good? Now we're going to add JavaScript to your HTML file. And an onclick JavaScript handler to your button.

<!DOCTYPE html>
<html>
   <head>
      <title>AJAX test</title>

      <script>
      function display()
     {
          var number=document.getElementById("txtNumber").value;
   
         if (window.XMLHttpRequest) 
         {// code for IE7+, Firefox, Chrome, Opera, Safari
             xmlhttp=new XMLHttpRequest();
         }
          else 
         {// code for IE6, IE5
             xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
         }
   
          xmlhttp.onreadystatechange=function() 
         {
             if (xmlhttp.readyState==4 && xmlhttp.status==200) 
            {
                document.getElementById("txtDisplay").value=xmlhttp.responseText;
            }
         }

          xmlhttp.open("GET","display.php?number="+number,true);
          xmlhttp.send();
      }

      function displaytimestwo()
     {
          var number=document.getElementById("txtDisplay").value;
   
         if (window.XMLHttpRequest) 
         {// code for IE7+, Firefox, Chrome, Opera, Safari
             xmlhttp=new XMLHttpRequest();
         }
          else 
         {// code for IE6, IE5
             xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
         }
   
          xmlhttp.onreadystatechange=function() 
         {
             if (xmlhttp.readyState==4 && xmlhttp.status==200) 
            {
                document.getElementById("txtDisplayTimesTwo").value=xmlhttp.responseText;
            }
         }

          xmlhttp.open("GET","displaytimestwo.php?number="+number,true);
          xmlhttp.send();
      }
      </script>
   </head>

   <body>
      <input type="text" id="txtNumber" value="0">
      <input type="button" value="Display" onclick="display();displaytimestwo();">
      <br />
      <input type="text" id="txtDisplay" value="0" disabled="disabled"> Original
      <br />
      <input type="text" id="txtDisplayTimesTwo" value="0" disabled="disabled"> x2
   </body>
</html> 

If you're familiar with AJAX, you will understand from the above that we've just added two JavaScript functions, each with a server-side call to their respective PHP files. Upon clicking the button, the value in the textbox will run these functions, one after the other.

Try it. What happens? A big fat nothing. The code was syntactically correct. But, the asynchronous nature of AJAX, so handy most times, works against you here. Instead of running in sequence, it tries to run display() and displaytimestwo() at the same time. And ends up clashing because displaytimestwo() and display() both require the object named xmlhttp! A solution here might be to use different variable names. But that's just a band-aid over the root of the problem. A far more elegant solution would be to force these two functions to run in their intended sequence. That's where your foundation in structured programming comes in.

Alter the code as follows:

<!DOCTYPE html>
<html>
   <head>
      <title>AJAX test</title>
      <script>
      function display()
     {
          var number=document.getElementById("txtNumber").value;
   
         if (window.XMLHttpRequest)
         {// code for IE7+, Firefox, Chrome, Opera, Safari
             xmlhttp=new XMLHttpRequest();
         }
          else
         {// code for IE6, IE5
             xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
         }
   
          xmlhttp.onreadystatechange=function()
         {
             if (xmlhttp.readyState==4 && xmlhttp.status==200)
            {
                document.getElementById("txtDisplay").value=xmlhttp.responseText;
                displaytimestwo();
            }
         }

          xmlhttp.open("GET","display.php?number="+number,true);
          xmlhttp.send();
      }

      function displaytimestwo()
     {
          var number=document.getElementById("txtDisplay").value;
   
         if (window.XMLHttpRequest)
         {// code for IE7+, Firefox, Chrome, Opera, Safari
             xmlhttp=new XMLHttpRequest();
         }
          else
         {// code for IE6, IE5
             xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
         }
   
          xmlhttp.onreadystatechange=function()
         {
             if (xmlhttp.readyState==4 && xmlhttp.status==200)
            {
                document.getElementById("txtDisplayTimesTwo").value=xmlhttp.responseText;
            }
         }

          xmlhttp.open("GET","displaytimestwo.php?number="+number,true);
          xmlhttp.send();
      }
      </script>
   </head>

   <body>
      <input type="text" id="txtNumber" value="0">
      <input type="button" value="Display" onclick="display();">
      <br />
      <input type="text" id="txtDisplay" value="0" disabled="disabled"> Original
      <br />
      <input type="text" id="txtDisplayTimesTwo" value="0" disabled="disabled"> x2
   </body>
</html> 

Now type "10" in the first textbox, click the button, and what do you get?


Original
x2

What we basically did was to move the displaytimestwo() command to the middle of the function display(), just after the part where the AJAX is successfully called. Which means displaytimestwo() runs only after display() runs!

Remember! When your code fails, make AJAX-ments!
T___T

No comments:

Post a Comment