PHP & 进度条阿贾克斯
我正在研究使用 ajax 请求和会话变量更新进度的进度条.当我的程序执行耗时的操作(例如发送许多电子邮件等)时,它只设置适当的会话变量(其中包含进度值).此操作由以下代码中的函数 post() 启动.
I am working on progress bar which updates progress using ajax requests and session variables. When my program performs time consuming operation such as sending many emails etc. it just set proper session variable (which contains progress value). This operation is started by function post() in code below.
同时,第二个函数 ask() 每 500 毫秒循环执行一次.它应该实时返回当前进度.这就是问题所在:ask() 发送的每个请求都在等待 post() 函数发送的请求完成.有趣的是,如果我设置了一些像 google.com 这样的 URL 而不是 url/to/progress 它工作得很好,只是它不是我想要的 :).这意味着问题出在服务器端.
In the meantime, the second function ask() is performed in loop every 500ms. It should return current progress in real time. And here is the problem: every request sent by ask() are waiting for request sent by post() function is finished. The funny part is that if I set some URL like google.com instead of url/to/progress it works just fine except that it is not what I want :). Which means that problem is on the server side.
不确定是否重要,但我使用 Yii 框架.
Not sure if it's important but I use Yii Framework.
下面的所有代码只是草稿(但有效),其唯一目的是表明我的意思.
All the code below is only scratch (but working) and its only purpose is to show what I meant.
提前致谢.
抱歉我的英语不好:)
查看部分:
<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;
}
推荐答案
我认为您的问题与会话有关.
I think your problem here is session related.
当脚本打开会话时,它会锁定会话文件.这意味着使用相同会话 ID 的任何后续请求都将排队,直到第一个脚本释放它对会话文件的锁定.您可以使用 session_write_close()
强制执行此操作 - 但这在这里不会真正帮助您,因为您正在尝试与会话文件共享进度信息,因此 post
脚本需要保持会话数据打开和可写.
When a script has an open session, it has a lock on the session file. This means that any subsequent requests which use the same session ID will be queued until the first script has released it's lock on the session file. You can force this with session_write_close()
- but this won't really help you here, as you are trying to share the progress info with the session file so post
script would need to keep the session data open and writable.
您需要想出另一种在 post
和 progress
脚本之间共享数据的方法 - 如果 post
已打开会话数据在整个执行过程中,progress
将永远无法访问会话数据,直到 post
执行完毕.也许您可以使用会话 ID 创建一个 post
具有写入权限的临时文件,您可以在其中放置进度指示器数据.progress
可以检查文件并返回该数据.IPC(进程间通信)有很多选项 - 这不是一个特别漂亮的选项,但它确实具有最大可移植性的优势.
You will need to come up with another way of sharing data between the post
and progress
scripts - if post
has the session data open throughout it's execution, progress
will never be able to access the session data until after post
has finished executing. Maybe you could use the session ID to create a temporary file which post
has write access to, in which you put the progress indicator data. The progress
can check the file and return that data. There are many options for IPC (inter-process communication) - this is not a particularly beautiful one but it does have the advantage of maximum portability.
附带说明 - 请不要将字符串传递给 setInterval()
,而是传递函数.所以你的行实际上应该是:
As a side note - please don't pass strings to setInterval()
, pass functions. So your line should actually read:
var progress = setInterval(ask, 500);
但是 - 在 ask()<的
success
/error
处理程序中使用 setTimeout()
会更好/code> ajax 函数.这是因为通过使用 setInterval()
,无论前一个请求的状态如何,都会发起新请求.在启动下一个请求之前等待前一个请求完成会更有效率.所以我会做更多这样的事情:
But - it would be better to use setTimeout()
in the success
/error
handlers of the ask()
ajax function. This is because by using setInterval()
, a new request will be initiated regardless of the state of the previous one. It would be more efficient to wait until the previous request has finished before initiating the next one. So I would do something more like this:
<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>
...虽然这仍然不能解决会话问题.
...although this still doesn't fix the session problem.
相关文章