Progress bar with php and ajax

I am working on a progress bar that updates progress using ajax requests and session variables. When my program runs a lot of time, for example, sends a lot of letters, etc., It simply sets the correct session variable (which contains the progress value). This operation is triggered by the post () function in the code below.

In the meantime, the second ask () function runs in a loop every 500 ms. It should return current progress in real time. And here's the problem: every request sent by ask () waits for a request sent by the post () function. The funny part is that if I set some kind of URL, for example google.com, and not url / to / progress, it works fine, except that this is not what I want :). This means that the problem is on the server side.

Not sure if this is important, but I am using the Yii Framework.

All of the code below is just a scratch (but it works), and its sole purpose is to show what I had in mind.

Thanks in advance.

Sorry for my bad 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:

 public function actionPost($time) { sleep(5); // time consuming operation echo $time . ' : ' . microtime(); exit; } public function actionProgress($time) { echo $time . ' : ' . microtime(); exit; } 
+7
source share
2 answers

I think your problem here is with the session.

When a script has an open session, it locks the session file. This means that any subsequent requests that use the same session identifier will be queued until the first script releases it in the session file. You can force this with session_write_close() - but that will not help you here, since you are trying to share progress information with the session file, so the post script will need to save the open and recorded session data.

You will need another way of exchanging data between post and progress scripts - if post contains session data that was opened during its execution, progress will never be able to access the session until post has completed execution. Perhaps you can use the session identifier to create a temporary file to which post has write access to which you place the progress indicator data. progress can check the file and return this data. There are many options for IPC (interprocess communication) - this is not particularly beautiful, but it has the advantage of maximum portability.

As a side note, do not pass setInterval() strings; pass functions. Therefore, your line should really read:

 var progress = setInterval(ask, 500); 

But - it would be better to use setTimeout() in the success / error handlers of the ajax ask() function. This is due to the fact that when using setInterval() new request will be initiated regardless of the state of the previous one. It would be more convenient to wait until the previous request completes before the next. So I would do something 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 does not fix the session problem.

+9
source

@DaveRandom above correctly indicates that you are a victim of session storage lock.

The workaround is pretty simple. You want to make a script that processes post() release session data locks so that the script that processes ask() can access the session data. You can do this with session_write_close .

The exact print here is that after calling session_write_close you will not have access to the session variables, so you need to structure the script for post accordingly:

  • Read all the data you need from $_SESSION and save it.
  • Call session_write_close to release the session lock.
  • Continue your long operation. If you need session data, output it from your own copy, not $_SESSION .

Alternatively, you can switch the session lock several times during the script 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 layout makes it so that while the script is sleeping, other scripts can easily access session data.

+6
source

All Articles