AJAX调用以(看似)同步方式返回结果

时间:2013-09-24 03:55:55

标签: php ajax apache yii

所有

如何检测问题的背景

我的问题涉及网络应用程序的性能,主要是索引页面。当我在我的公司的一个本地分支机构进行演示时,我注意到了这个问题,因为谷歌需要大约10秒的加载速度来判断互联网速度缓慢(我不知道确切的速度或ping速率)。我的索引页面加载时间缩短了大约10-20倍。我假设我的应用程序完成了服务器端的大部分工作(因为php正在进行所有的数据库查询......)。但是这让我看看Chrome的网络工具,看看这些4个div的延迟时间是由ajax加载的(我稍后会详细说明)。有趣的是,被调用的脚本似乎按顺序运行,但不一定按照我调用ajax调用的顺序运行(有时它们会执行,有时则不会)。

这些div / ajax请求是什么?

以下是请求的代码段:

    Yii::app()->clientScript->registerScript('leftDiv', '
    $( "#left_dash" ).load(
        "'.$this->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();
        }
     );
 ' );

这是请求的页面:

$this->widget('widgets.ScrollList',array(
    'condition'=> 
        function($muddJob,$scrollList) 
        {
              $job = $muddJob->Job;; //returns a job or empty array
              if(!empty($job) )
              {
                 if( $muddJob->uploadArtwork == null && $muddJob->uploadData == null  ) {
                     array_push($scrollList->_models,$job);
                     $scrollList->columnValues = array($muddJob->jobDescription,$muddJob->dropDate1);
                     return true;
                 }

              }

              return false;

        },
     'columns' => array('col1'=>"MuddJob#",'col2'=>"Desc",'col3'=>"Dealer Name"),
     'name'=> "Print New Ticket",
     'muddJobs' => $currentExchanges->getCurrentMuddExchanges(),

        )
    ); 

想象一下该页面(ajax调用的页面)有6个类似的声明来创建小部件。目标是返回html以代替索引页面上的加载gif。

这是滚动小部件:

<?php
Yii::import('widgets.ScrollListBase');
include_once Yii::app()->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->init();
        $this->_listInfo = $this->generateListInfo($this->columns);
        //$muddJobs = $this->getCurrentMuddExchanges(); 
        $listInfo = $this->newScrollList($this->muddJobs);
        $contents = $this->createContent($listInfo,$this->name);
        $this->_content = $contents[0];
//        $this->_fullTableContent = $contents[1];
        //$this->_listInfo = $contents[2];
    }

   public function run()
   {
       //if($this->data['isVisible']) 
       echo $this->_content;
       Yii::app()->session["exploded_content_{$this->name}"] = $this->_models;
   }
    private function newScrollList($muddJobs)
        {
                $listInfo = $this->_listInfo;
                $tempCount = 0;


                foreach($muddJobs as $muddJob) 
                    {

                        $condition = $this->condition;

                        if($condition($muddJob,$this) && empty($this->jobsMailingTodayArray) ) //if no job exists for the muddExchange...
                        {
                            $tempArray = $this->createWidgetLinks($tempCount,$listInfo,$muddJob,$this->columnValues);
                            $listInfo = $tempArray[0];
                            $tempCount = $tempArray[1];

                        }

                        elseif ( !empty($this->jobsMailingTodayArray ) )
                        {
                            foreach ($this->jobsMailingTodayArray as $jobMailingToday)     //change to for loop over the length of the jobsMailingToday
                            {
                                $tempArray = $this->createWidgetLinks($tempCount,$listInfo,$muddJob,$this->columnValues);
                                $listInfo = $tempArray[0];
                                $tempCount = $tempArray[1];
                            }
                            $this->jobsMailingTodayArray = array();
                        }

                    }

                return array($listInfo,$tempCount);


        }

    }
?>

这是它的父母:

<?php


class ScrollListBase extends CWidget
{       

        private $content = "<p>";
        private $divDeclaration = "<div class='scroll100'>\n<table class='quickInfoTable'>\n<thead>\n";
        private $headTag = "<th>";
        private $headTagClose = "</th>\n";
        private $theadTagClose = "</thead>\n";
        private $bodyTag = "<tbody>\n";
        private $listInfo = "<div class='scroll100'>\n<table class='quickInfoTable'>\n<thead>\n<th>Job#</th>\n<th>Package#</th>\n<th>Entry Date</th>\n</thead>\n<tbody>\n";


        /**
     * Initializes the widget.
     */

        public function createContent($listInfo,$name)
        {

                $largeHref = Yii::app()->request->baseUrl . '/index.php/site/fullTableView';

                $this->content .= "<span class='badge' >{$listInfo[1]}  </span> <a href='#'>{$name} </a> <a href='$largeHref/Name/{$name}'> <small>(view larger)</small> </a> </p>";


                if( $listInfo[1] > 0 ) 
                {
//                    $this->fullTable .= substr($listInfo[0],22);            
//                    $this->fullTableContent= $this->fullContent .= $this->fullTable . "</tbody>\n</table>\n</div>";

                    $this->content .= $listInfo[0] . "</tbody>\n</table>\n</div>";
                }

            return array($this->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()->with('MuddExchange')->findByAttributes($attributeArray);

        }


        protected function createWidgetLinks($tempCount,$listInfo,$muddJob,$columnValues,$url="/MuddExchange/")
       {
            $tempCount++;
            $viewIndex = $muddJob->exchange_id;
            $model = $muddJob;
            $job = $muddJob->Job;
            if ( isset($job ))
            {
                $model = $job;
                $url = "/Jobs/";
                $viewIndex = $model->job_id;
            }
            $link = CHtml::link("$model->jobNumber",array("{$url}{$viewIndex}"));
            $listInfo .= "<tr>\n<td>$link</td>\n";

            foreach ($columnValues as $columnValue)
            {
                $listInfo .= "<td>{$columnValue}</td>\n";
            }

            $listInfo .= "</tr>";

            return array($listInfo,$tempCount);
       }



       protected function getListInfo()
       {
           return $this->listInfo;
       }

       /**
        * Takes an array of strings to generate the column names for a particular list.
        * @param array $heads
        * @return string 
        * 
        */
       protected function generateListInfo($heads) 
       {
           //<th>Job#</th>\n<th>Package#</th>\n<th>Entry Date</th>\n</thead>\n<tbody>\n";
           $htmlScrollStart = $this->divDeclaration;

           foreach ($heads as $tableColumn => $name)
           {
               $htmlScrollStart .= $this->headTag . $name . $this->headTagClose;
           }

           $htmlScrollStart .= $this->theadTagClose . $this->bodyTag;

           return $htmlScrollStart;
       }





    public function calculateDueDate($jobsMailDate,$job)
    {
        $package = PackageSchedule::model()->findByAttributes(array('package_id'=>$job->packageID));
        $projectedDays = $package->projected_days_before_mail_date;

        $dropDate1 = $jobsMailDate->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->difference($dueDate);


        }while( $difference != $projectedDays);
        return $dueDate;
    }
}
?>

为什么我认为这种行为很奇怪

整个慢速互联网事物本身就是一头野兽,但我不认为这是在StackOverflow的范围内。我更担心这些div的加载。最后加载的div,即平均花费1.5到2秒,是对创建单个小部件的页面的ajax请求。背后的逻辑是:

<?php
 include_once Yii::app()->extensionPath . "/CurrentExchanges.php"; 
 $currentExchanges = Yii::app()->session['currentExchanges'];
 $this->layout = 'barebones';
 $this->widget('widgets.ScrollList',array(
    'condition'=> 
        function($muddJob,$scrollList) 
        {
          if ($muddJob->dropDate1 != null && $muddJob->dropDate1 != '0000-00-00')
            {
                $job = $muddJob->Job;;
                if(!empty($job) && $job->packageID != null) //if job exists for the muddExchange and has a package
                {
                    if($job->uploadArtwork == null )
                    {
                        $jobsMailDate = JobsMailDate::model()->findByAttributes(array("job_id"=>$job->job_id,'sequence_num'=>1));
                        if(!empty($jobsMailDate))
                            {
                               $calculatedDueDate = $scrollList->calculateDueDate($jobsMailDate,$job);                                
                                if (strtotime($calculatedDueDate) <= strtotime(date("Y-m-d")) )
                                {
                                    array_push($scrollList->_models , $job);
                                    $scrollList->columnValues = array($muddJob->jobDescription,$muddJob->dropDate1,$jobsMailDate->projected_mail_date);
                                    return true;
                                }
                            }
                    }
                }
            }
            return false;
        },
     'columns' => array('col1'=>"MuddJob#",'col2'=>"Desc",'col3'=>"Drop Date", 'col4' =>'Projected Drop Date'),
     'name'=> "Artwork Due Today",
     'muddJobs' => $currentExchanges->getCurrentMuddExchanges(),

        )
    );
?>

calculateduedate方法对服务器进行了2次额外调用。

我无法理解的是为什么左边的div(最需要处理的)通常是第一个返回的,而artLoad通常是最后一个加载(通过实质性差异)。以下是chromes网络工具返回的一些时间:

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

我无法理解为什么左/右仪表板的返回速度比artLoad快得多。除了实际比较(if if语句)之外,artLoad和dataLoad的代码几乎相同。如果这是真正的异步,我希望订单是art / dataLoad,rightDashLoad和leftDashLoad,完全基于每个页面中完成的计算量。也许服务器不是多线程,或者有一些奇怪的配置,但如果是这种情况,我不明白为什么加载的影响会被慢速互联网这么难打。 如果我忽略了一些显而易见的事情或未能恰当地使用谷歌,我会道歉。感谢您提供的任何帮助!

语言/其他技术信息

该应用程序是使用Yii框架开发的。 PHP 5.3。 Apache服务器。 INNODB表。服务器托管在dreamhost中。

更新

我更改了视图页面,以便ajax调用现在调用控制器操作。它似乎使我的本地开发环境中的加载时间更加相似(异步?),但在QA环境(托管在dreamhost上...)上真的减慢了它的速度。以下是本地网络工具信息的屏幕截图:

dev environment

和qa网站(注意,数据库具有大约相同数量的数据......)

qa environment

思考?在我看来,我的原始问题可能会得到解决,因为返回时间(在我的本地开发站上)看起来更像我期望的那样。

另外,由于xdebug正在使用会话,我自己对NetBeans调试如何在这个同步加载事件中起作用的无知。我相信这迫使ajax呼叫等待轮到他们。

1 个答案:

答案 0 :(得分:1)

感谢@Rowan帮助我诊断这种奇怪的行为。 PHP试图在会议结束前请求会话,以防止发生种族危害。我的代码中有会话请求,我的IDE(NetBeans)启动了一个会话。删除对ajax调用页面中的会话的所有引用,并使用使用renderPartial()的ajax调用操作证明返回预期的行为(以及更好的代码IMO)。让我知道如何在StackOverflow方面正确地感谢用户(我可以发表评论,还是有什么可用?)。谢谢你们!