[英]Progress bar with PHP & Ajax
I am working on progress bar which updates progress using ajax requests and session variables. 我正在使用进度条,使用ajax请求和会话变量更新进度。 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.
此操作由下面的代码中的函数post()启动。
In the meantime, the second function ask() is performed in loop every 500ms. 同时,第二个函数ask()每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.
这就是问题:ask()发送的每个请求都在等待post()函数发送的请求完成。 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 :).
有趣的是,如果我设置一些像google.com这样的URL而不是url / to / progress它可以正常工作,除非它不是我想要的:)。 Which means that problem is on the server side.
这意味着问题出在服务器端。
Not sure if it's important but I use Yii Framework. 不确定它是否重要,但我使用的是Yii Framework。
All the code below is only scratch (but working) and its only purpose is to show what I meant. 下面的所有代码只是划痕(但有效),其唯一目的是展示我的意思。
Thanks in advance. 提前致谢。
Sorry for my poor english :) 抱歉我的英语不好:)
View part: 查看部分:
<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>
Controller part: 控制器部分:
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. 我认为你的问题与会话有关。
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.
这意味着任何使用相同会话ID的后续请求都将排队,直到第一个脚本释放它对会话文件的锁定为止。 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. 您可以使用
session_write_close()
强制执行此操作 - 但这不会对您有所帮助,因为您尝试与会话文件共享进度信息,因此post
脚本需要保持会话数据的开放性和可写性。
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. 您需要提出另一种在
post
和progress
脚本之间共享数据的方法 - 如果post
在整个执行期间打开了会话数据,那么在post
完成执行之后, progress
将永远无法访问会话数据。 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. 也许你可以使用会话ID来创建该临时文件
post
已至,在你把进度指示器数据写入访问。 The progress
can check the file and return that data. progress
可以检查文件并返回该数据。 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. IPC(进程间通信)有很多选择 - 这不是一个特别漂亮的选项,但它确实具有最大的可移植性。
As a side note - please don't pass strings to setInterval()
, pass functions. 作为旁注 - 请不要将字符串传递给
setInterval()
,传递函数。 So your line should actually read: 所以你的行实际上应该是:
var progress = setInterval(ask, 500);
But - it would be better to use setTimeout()
in the success
/ error
handlers of the ask()
ajax function. 但是 - 最好在
ask()
ajax函数的success
/ error
处理程序中使用setTimeout()
。 This is because by using setInterval()
, a new request will be initiated regardless of the state of the previous one. 这是因为通过使用
setInterval()
,无论前一个请求的状态如何,都将启动一个新请求。 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. ...虽然这仍然无法解决会话问题。
@DaveRandom above correctly points out that you are a victim of a session storage lock. 上面的@DaveRandom正确地指出你是会话存储锁的受害者。
The workaround is quite simple. 解决方法非常简单。 You want to make the script that processes
post()
release the lock on the session data so that the script that handles ask()
can access this session data. 您希望使处理
post()
的脚本释放对会话数据的锁定,以便处理ask()
的脚本可以访问此会话数据。 You can do that with session_write_close
. 你可以使用
session_write_close
来做到这一点。
The fine print here is that after calling session_write_close
you will not have access to session variables, so you need to structure the script for post
accordingly: 这里的精细打印是,在调用
session_write_close
您将无法访问会话变量,因此您需要相应地构建脚本以进行post
:
$_SESSION
and save a copy of it. $_SESSION
读取您需要的所有数据并保存它的副本。 session_write_close
to release the session lock. session_write_close
以释放会话锁定。 $_SESSION
directly. $_SESSION
。 Alternatively, you can toggle the lock on the session multiple times during the script's lifetime: 或者,您可以在脚本的生命周期内多次切换会话锁定:
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'];
This arrangement makes it so that while the script it sleeping, other scripts can access the session data without problems. 这种安排使得当它睡眠的脚本时,其他脚本可以毫无问题地访问会话数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.