title: AJAX calls return results in a (seemingly) synchronous manner
body: <p>All,</p>
  
  <h2>Background of how problem was detected</h2>
  
  <p>My question concerns the performance of a web app, mainly the index page. I noticed the problem when I was giving a demonstration at a local branch of my company that has slow internet (I don't know the exact speeds, or ping rate) judged by the fact that Google took about 10 seconds to load. My index page took ~10-20 times longer to load. I was under the assumption that my app did most of the work on the server side (as php is making all of the database queries...). But this led me to look at the network tool of Chrome and see the latency times of these 4 divs being loaded by ajax (I'll elaborate in a bit). Interestingly, the scripts being called appear to run sequentially, but not necessarily in the order I invoked the ajax calls (sometimes they do, other times they don't). </p>
  
  <h2>What are these divs / ajax requests?</h2>
  
  <p>Here is a code snippets of a request:</p>
  
  <pre><code>    Yii::app()-&gt;clientScript-&gt;registerScript('leftDiv', '
      $( "#left_dash" ).load(
          "'.$this-&gt;createUrl("/site/page?view=leftDashLoad") .'", 
           function(){ 
              $("#left_dash p a").click(function() {
                  $(this).parent().parent().find("div.scroll100").slideUp();
                  $(this).parent().next().stop(false, false).slideDown();
              });
  
              $("p:first-child").next().slideDown();
          }
       );
   ' );
  </code></pre>
  
  <p>Here is the page requested:</p>
  
  <pre><code>$this-&gt;widget('widgets.ScrollList',array(
      'condition'=&gt; 
          function($muddJob,$scrollList) 
          {
                $job = $muddJob-&gt;Job;; //returns a job or empty array
                if(!empty($job) )
                {
                   if( $muddJob-&gt;uploadArtwork == null &amp;&amp; $muddJob-&gt;uploadData == null  ) {
                       array_push($scrollList-&gt;_models,$job);
                       $scrollList-&gt;columnValues = array($muddJob-&gt;jobDescription,$muddJob-&gt;dropDate1);
                       return true;
                   }
  
                }
  
                return false;
  
          },
       'columns' =&gt; array('col1'=&gt;"MuddJob#",'col2'=&gt;"Desc",'col3'=&gt;"Dealer Name"),
       'name'=&gt; "Print New Ticket",
       'muddJobs' =&gt; $currentExchanges-&gt;getCurrentMuddExchanges(),
  
          )
      ); 
  </code></pre>
  
  <p>Imagine that page (the page that ajax has called) having 6 similar declarations that create widgets. The goal is to return html to put back in place of a loading gif on the index page. </p>
  
  <p>Here is the scroll widget:</p>
  
  <pre><code>&lt;?php
  Yii::import('widgets.ScrollListBase');
  include_once Yii::app()-&gt;extensionPath . "/BusinessDay.php"; 
  
  class ScrollList extends ScrollListBase
  {
      private $_content;
      public $columns = array();
      public $columnValues;
      private  $_listInfo;
      public $name;
      public $_models = array();
      public $condition; 
      public $muddJobs; //object to pass
      public $jobsMailingTodayArray = array();
  
  
  public function init()
      {
          //$this-&gt;init();
          $this-&gt;_listInfo = $this-&gt;generateListInfo($this-&gt;columns);
          //$muddJobs = $this-&gt;getCurrentMuddExchanges(); 
          $listInfo = $this-&gt;newScrollList($this-&gt;muddJobs);
          $contents = $this-&gt;createContent($listInfo,$this-&gt;name);
          $this-&gt;_content = $contents[0];
  //        $this-&gt;_fullTableContent = $contents[1];
          //$this-&gt;_listInfo = $contents[2];
      }
  
     public function run()
     {
         //if($this-&gt;data['isVisible']) 
         echo $this-&gt;_content;
         Yii::app()-&gt;session["exploded_content_{$this-&gt;name}"] = $this-&gt;_models;
     }
      private function newScrollList($muddJobs)
          {
                  $listInfo = $this-&gt;_listInfo;
                  $tempCount = 0;
  
  
                  foreach($muddJobs as $muddJob) 
                      {
  
                          $condition = $this-&gt;condition;
  
                          if($condition($muddJob,$this) &amp;&amp; empty($this-&gt;jobsMailingTodayArray) ) //if no job exists for the muddExchange...
                          {
                              $tempArray = $this-&gt;createWidgetLinks($tempCount,$listInfo,$muddJob,$this-&gt;columnValues);
                              $listInfo = $tempArray[0];
                              $tempCount = $tempArray[1];
  
                          }
  
                          elseif ( !empty($this-&gt;jobsMailingTodayArray ) )
                          {
                              foreach ($this-&gt;jobsMailingTodayArray as $jobMailingToday)     //change to for loop over the length of the jobsMailingToday
                              {
                                  $tempArray = $this-&gt;createWidgetLinks($tempCount,$listInfo,$muddJob,$this-&gt;columnValues);
                                  $listInfo = $tempArray[0];
                                  $tempCount = $tempArray[1];
                              }
                              $this-&gt;jobsMailingTodayArray = array();
                          }
  
                      }
  
                  return array($listInfo,$tempCount);
  
  
          }
  
      }
  ?&gt;
  </code></pre>
  
  <p>Here is it's parent:</p>
  
  <pre><code>&lt;?php
  
  
  class ScrollListBase extends CWidget
  {       
  
          private $content = "&lt;p&gt;";
          private $divDeclaration = "&lt;div class='scroll100'&gt;\n&lt;table class='quickInfoTable'&gt;\n&lt;thead&gt;\n";
          private $headTag = "&lt;th&gt;";
          private $headTagClose = "&lt;/th&gt;\n";
          private $theadTagClose = "&lt;/thead&gt;\n";
          private $bodyTag = "&lt;tbody&gt;\n";
          private $listInfo = "&lt;div class='scroll100'&gt;\n&lt;table class='quickInfoTable'&gt;\n&lt;thead&gt;\n&lt;th&gt;Job#&lt;/th&gt;\n&lt;th&gt;Package#&lt;/th&gt;\n&lt;th&gt;Entry Date&lt;/th&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n";
  
  
          /**
       * Initializes the widget.
       */
  
          public function createContent($listInfo,$name)
          {
  
                  $largeHref = Yii::app()-&gt;request-&gt;baseUrl . '/index.php/site/fullTableView';
  
                  $this-&gt;content .= "&lt;span class='badge' &gt;{$listInfo[1]}  &lt;/span&gt; &lt;a href='#'&gt;{$name} &lt;/a&gt; &lt;a href='$largeHref/Name/{$name}'&gt; &lt;small&gt;(view larger)&lt;/small&gt; &lt;/a&gt; &lt;/p&gt;";
  
  
                  if( $listInfo[1] &gt; 0 ) 
                  {
  //                    $this-&gt;fullTable .= substr($listInfo[0],22);            
  //                    $this-&gt;fullTableContent= $this-&gt;fullContent .= $this-&gt;fullTable . "&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;/div&gt;";
  
                      $this-&gt;content .= $listInfo[0] . "&lt;/tbody&gt;\n&lt;/table&gt;\n&lt;/div&gt;";
                  }
  
              return array($this-&gt;content);
          }
  
  //Helper Methods
  
          /**
           * 
           * @param type $attributeArray. send an accociative array
           * @return type = either a job or an empty array
           */
          protected function getJobByAttributes($attributeArray)
          {
              return Jobs::model()-&gt;with('MuddExchange')-&gt;findByAttributes($attributeArray);
  
          }
  
  
          protected function createWidgetLinks($tempCount,$listInfo,$muddJob,$columnValues,$url="/MuddExchange/")
         {
              $tempCount++;
              $viewIndex = $muddJob-&gt;exchange_id;
              $model = $muddJob;
              $job = $muddJob-&gt;Job;
              if ( isset($job ))
              {
                  $model = $job;
                  $url = "/Jobs/";
                  $viewIndex = $model-&gt;job_id;
              }
              $link = CHtml::link("$model-&gt;jobNumber",array("{$url}{$viewIndex}"));
              $listInfo .= "&lt;tr&gt;\n&lt;td&gt;$link&lt;/td&gt;\n";
  
              foreach ($columnValues as $columnValue)
              {
                  $listInfo .= "&lt;td&gt;{$columnValue}&lt;/td&gt;\n";
              }
  
              $listInfo .= "&lt;/tr&gt;";
  
              return array($listInfo,$tempCount);
         }
  
  
  
         protected function getListInfo()
         {
             return $this-&gt;listInfo;
         }
  
         /**
          * Takes an array of strings to generate the column names for a particular list.
          * @param array $heads
          * @return string 
          * 
          */
         protected function generateListInfo($heads) 
         {
             //&lt;th&gt;Job#&lt;/th&gt;\n&lt;th&gt;Package#&lt;/th&gt;\n&lt;th&gt;Entry Date&lt;/th&gt;\n&lt;/thead&gt;\n&lt;tbody&gt;\n";
             $htmlScrollStart = $this-&gt;divDeclaration;
  
             foreach ($heads as $tableColumn =&gt; $name)
             {
                 $htmlScrollStart .= $this-&gt;headTag . $name . $this-&gt;headTagClose;
             }
  
             $htmlScrollStart .= $this-&gt;theadTagClose . $this-&gt;bodyTag;
  
             return $htmlScrollStart;
         }
  
  
  
  
  
      public function calculateDueDate($jobsMailDate,$job)
      {
          $package = PackageSchedule::model()-&gt;findByAttributes(array('package_id'=&gt;$job-&gt;packageID));
          $projectedDays = $package-&gt;projected_days_before_mail_date;
  
          $dropDate1 = $jobsMailDate-&gt;projected_mail_date;
          $dropDate = wrapBusinessDay($dropDate1); //use this for actual command...  
          $toSec = 24*60*60;
          $dayInt =0;
          $secDropDate = strtotime($dropDate1);
  
          do{
             $dayInt +=1; 
             $daysInSec = ($dayInt)  * $toSec ;
             $secGuessDueDate = $secDropDate - $daysInSec; 
             $dueDate = date('Y-m-d',$secGuessDueDate);
  
             $difference = $dropDate-&gt;difference($dueDate);
  
  
          }while( $difference != $projectedDays);
          return $dueDate;
      }
  }
  ?&gt;
  </code></pre>
  
  <h2>Why I think this behavior is odd</h2>
  
  <p>The whole slow internet thing is a beast in and of itself, but I don't think that is in the scope of StackOverflow. I'm more concerned about the loading of these divs. The div that loads last, i.e., takes on average 1.5 to 2 seconds, is an ajax request to a page that creates a single widget. The logic behind it is here:</p>
  
  <pre><code>&lt;?php
   include_once Yii::app()-&gt;extensionPath . "/CurrentExchanges.php"; 
   $currentExchanges = Yii::app()-&gt;session['currentExchanges'];
   $this-&gt;layout = 'barebones';
   $this-&gt;widget('widgets.ScrollList',array(
      'condition'=&gt; 
          function($muddJob,$scrollList) 
          {
            if ($muddJob-&gt;dropDate1 != null &amp;&amp; $muddJob-&gt;dropDate1 != '0000-00-00')
              {
                  $job = $muddJob-&gt;Job;;
                  if(!empty($job) &amp;&amp; $job-&gt;packageID != null) //if job exists for the muddExchange and has a package
                  {
                      if($job-&gt;uploadArtwork == null )
                      {
                          $jobsMailDate = JobsMailDate::model()-&gt;findByAttributes(array("job_id"=&gt;$job-&gt;job_id,'sequence_num'=&gt;1));
                          if(!empty($jobsMailDate))
                              {
                                 $calculatedDueDate = $scrollList-&gt;calculateDueDate($jobsMailDate,$job);                                
                                  if (strtotime($calculatedDueDate) &lt;= strtotime(date("Y-m-d")) )
                                  {
                                      array_push($scrollList-&gt;_models , $job);
                                      $scrollList-&gt;columnValues = array($muddJob-&gt;jobDescription,$muddJob-&gt;dropDate1,$jobsMailDate-&gt;projected_mail_date);
                                      return true;
                                  }
                              }
                      }
                  }
              }
              return false;
          },
       'columns' =&gt; array('col1'=&gt;"MuddJob#",'col2'=&gt;"Desc",'col3'=&gt;"Drop Date", 'col4' =&gt;'Projected Drop Date'),
       'name'=&gt; "Artwork Due Today",
       'muddJobs' =&gt; $currentExchanges-&gt;getCurrentMuddExchanges(),
  
          )
      );
  ?&gt;
  </code></pre>
  
  <p>The calculateduedate method makes 2 additional calls to the server.</p>
  
  <p>What I'm failing to understand is why the left div (with the most proccessing to do) is usually the first to return and the artLoad is usually the last to load (by a substantial difference). Here are some times returned by chromes network tool:</p>
  
  <pre><code>leftDashLoad:  475ms
  rightDashLoad: 593ms
  dataLoad:      825ms
  artLoad:       1.41s
  
  dataLoad:      453ms
  rightDashLoad: 660ms
  leftDashLoad:  919ms
  artLoad:       1.51s
  
  rightDashLoad: 559ms
  leftDashLoad:  1.17s
  dataLoad:      1.65s  
  artLoad:       2.01s
  </code></pre>
  
  <p>I just can't fathom why the left/right dashloads return so much faster than the artLoad. The code for artLoad and dataLoad are nearly identical save the actual comparison (the one if statement). If this were truly asynchronous, I'd expect the order to be art/dataLoad, rightDashLoad and leftDashLoad based purely on the amounts of computation done in each page. Perhaps the server isn't multithreading, or there is some weird configuration, but if that were the case, I don't see why the effects of the loading would be hit so hard by slow internet.
  If I have overlooked something obvious or failed to use google appropriately, I do apologize. Thanks for any help you can offer!</p>
  
  <h2>Language/other tech info</h2>
  
  <p>The app was developed using the Yii framework. PHP 5.3. Apache Server. INNODB Tables. Server is hosted in dreamhost.</p>
  
  <h2>Update</h2>
  
  <p>I've changed the view page so that the ajax calls are now calling a controller action. It seems to have made the loading times more similar (asynchronous?) on my local dev environment, but really slowed it down on the QA environment (hosted on dreamhost...). Here is screen shot of the local network tools info:</p>
  
  <p><a href="http://grab.by/qAu4" rel="nofollow">dev environment</a></p>
  
  <p>and the qa site (note, that the databases have about the same amounts of data...)</p>
  
  <p><a href="http://grab.by/qAuS" rel="nofollow">qa environment</a></p>
  
  <p>Thoughts? It seems to me that my original problem may be solved, as the return times (on my local dev) look more like I expect them to.</p>
  
  <p>Also, my own ignorance as to how NetBeans debugs was playing a part in this synchronous loading thing as xdebug is using a session. I believe this was forcing the ajax calls to wait their turn.</p>