2012-02-16 104 views
7

我正在研究使用ajax请求和会话变量更新进度的进度条。当我的程序执行耗时的操作时,比如发送很多电子邮件等,它只需设置适当的会话变量(包含进度值)。此操作由以下代码中的函数post()开始。PHP和Ajax进度条

与此同时,第二个函数ask()每500ms循环执行一次。它应该实时返回当前的进度。问题在于:ask()发送的每个请求都在等待post()函数发送的请求完成。有趣的是,如果我设置一些URL,如google.com而不是url/to/progress,它的工作原理很好,除非它不是我想要的:)。这意味着问题出在服务器端。

不确定它是否重要,但我使用Yii Framework。

下面的所有代码只是从头开始(但工作),它的唯一目的是显示我的意思。

在此先感谢。

对不起,我的英文不好:)

查看部分:

<script type="text/javascript"> 
function ask() { 
    var d = new Date(); 
    var time = d.getTime(); 
    $.ajax({ 

    type: 'get', 
    url: '/url/to/progress' + '?time=' + time, 
    success: function(data) { 
     $("#progress").html(data); 
    } 
    }) 
} 

function post() { 
    var d = new Date(); 
    var time = d.getTime(); 

    $.ajax({ 
     type: 'post', 
     url: '/url/to/post' + '?time=' + time, 
     data: {"some": "data"}, 
     success: function(data) {alert(data)} 
    }); 
} 

$("#test").click(
    function() { 
    post(); 
    var progress = setInterval("ask();", 500); 
    } 
); 
</script> 

控制器部分:

public function actionPost($time) { 
    sleep(5); // time consuming operation 
    echo $time . ' : ' . microtime(); 
    exit; 
} 

public function actionProgress($time) { 
    echo $time . ' : ' . microtime(); 
    exit; 
} 
+0

你从服务器获得的回应是什么?检查开发工具>网络> xhr请求 – 2012-02-16 14:07:04

回答

9

我想在这里你的问题是会话相关的。

当脚本有一个打开的会话时,它会锁定会话文件。这意味着任何使用相同会话ID的后续请求都将排队,直到第一个脚本释放它对会话文件的锁定。您可以用session_write_close()强制执行此操作 - 但这不会对您有所帮助,因为您正尝试与会话文件共享进度信息,因此post脚本需要保持会话数据的可打开和可写。

您将需要拿出postprogress脚本之间共享数据的另一种方式 - 如果post拥有遍布它的执行开放会话数据,progress将永远无法访问会话数据,直到后post执行完毕。也许你可以使用会话ID来创建一个临时文件,其中post有写入访问权限,其中可以放入进度指示器数据。 progress可以检查文件并返回该数据。 IPC(进程间通信)有许多选项 - 这不是一个非常漂亮的选项,但它具有最大可移植性的优点。

作为一个方面说明 - 请不要传递字符串到setInterval(),传递函数。所以,你行应实际阅读:

var progress = setInterval(ask, 500); 

但是 - 这将是更好的ask() AJAX功能的success/error处理程序使用setTimeout()。这是因为通过使用setInterval(),无论前一个状态如何,都会启动一个新请求。在启动下一个请求之前等待前一个请求结束会更有效率。所以我会做更像这样的事情:

<script type="text/javascript"> 

    // We'll set this to true when the initail POST request is complete, so we 
    // can easily know when to stop polling the server for progress updates 
    var postComplete = false; 

    var ask = function() { 

    var time = new Date().getTime(); 

    $.ajax({ 

     type: 'get', 
     url: '/url/to/progress' + '?time=' + time, 

     success: function(data) { 
     $("#progress").html(data); 
     if (!postComplete) 
      setTimeout(ask, 500); 
     } 
     }, 
     error: function() { 
     // We need an error handler as well, to ensure another attempt gets scheduled 
     if (!postComplete) 
      setTimeout(ask, 500); 
     } 
     } 

    }); 

    } 

    $("#test").click(function() { 

    // Since you only ever call post() once, you don't need a seperate function. 
    // You can just put all the post() code here. 

    var time = new Date().getTime(); 

    $.ajax({ 

     type: 'post', 
     url: '/url/to/post' + '?time=' + time, 
     data: { 
     "some": "data" 
     }, 

     success: function(data) { 
     postComplete = true; 
     alert(data); 
     } 
     error: function() { 
     postComplete = true; 
     } 

    }); 

    if (!postComplete) 
     setTimeout(ask, 500); 
    } 

    }); 

</script> 

......虽然这仍然不能解决会话问题。

+0

您是可靠的。我发现函数session_write_close(),它似乎工作!我所需要做的只是在动作开始时关闭会话,并重新打开它仅用于更新会话变量。非常感谢,感谢您的建议。 – pawelo 2012-02-16 14:13:51

+0

小心在同一个脚本中多次打开和关闭会话数据 - 我看到有人抱怨说它不能可靠地工作,所以要确保你彻底测试了你所做的任何事情。 – DaveRandom 2012-02-16 14:15:18

+0

嗯,也许更好的解决方案是在Db或文件中存储进度变量。我必须检查出来,谢谢! – pawelo 2012-02-16 14:18:58

6

@DaveRandom上面正确地指出你是会话存储锁定的受害者。

解决方法非常简单。您希望使处理post()的脚本释放会话数据的锁定,以便处理ask()的脚本可以访问此会话数据。你可以用session_write_close来做到这一点。

小字这里是调用session_write_close后,你将不能访问会话变量,所以需要相应地结构中的脚本post

  1. 阅读所有数据,您将需要从$_SESSION和保存它的副本。
  2. 拨打session_write_close释放会话锁定。
  3. 继续您的冗长操作。如果您需要会话数据,请直接从您自己的副本中取出而不是$_SESSION

或者,您可以在脚本的生命周期内切换会议上多次锁定:

session_start(); 
$_SESSION['name'] = 'Jon'; 

// Quick operation that requires session data 
echo 'Hello '.$_SESSION['name']; 

// Release session lock 
session_write_close(); 

// Long operation that does not require session data. 
sleep(10); 

// Need access to session again 
session_start(); 
echo 'Hello again '.$_SESSION['name']; 

这种布置,这样当它睡觉的脚本,其他脚本可以访问会话数据,而不问题。

+0

我差点说“只是在'post'脚本中使用'session_write_close()',直到我重新读这个问题,并意识到他正在尝试通过会话数据共享漫长操作的进度。显然,你不能这样做。当您需要更新进度信息时,您可以尝试'session_write_close()'和随后的'session_start()' - 我从来没有尝试过这个过程,但我记得在这里看到一个问题,有人发现这样做没有用,所以如果它能在任何地方工作,它可能无处不在。因此'你需要想出另一种分享数据的方式' – DaveRandom 2012-02-16 14:13:45