PHP / JS progress bar

I am trying to create a page that will generate a set of results from a complex database query and php analysis ... but this is mostly irrelevant ... The main thing is that it takes a minute or two full, and I hope to display a progress bar , not the general graphic animation "loading ...".

The breakdown will be ...

  • The user opens page A.
  • Page A requests data from page B (most likely AJAX).
  • Page B processes 100,000+ records or so in the database and analyzes them.
  • Page A shows a progress bar that shows how far the process has progressed.
  • Page B returns a result set.
  • Page A displays a result set.

I know how to return data to an ajax request, but my problem is that I don’t know how to continuously return data to show the status of the process (for example,% of scanned lines).

I looked at EventSource / Server-Sent-Events, which shows a promise, I'm just not too sure how to make it work correctly, or if there is a better way to do this.

I tried to make a small small mock page using only EventSource, fine, but when I laid it out to call eventSource (the page that controls the session variable to change) and the ajax request (the actual data transfer / return), it falls apart.

I will probably miss something obvious or do something stupid, but that’s most of what I have ... Any help, suggestions, tips or even suggestions on completely different ways to do this would be awesome :)

User Page:

<!DOCTYPE html> <html> <head> <title>Dynamic Progress Bar Example</title> <script src="script.js"></script> </head> <body> <input type="button" value="Submit" onclick="connect()" /> <progress id='progressor' value="0" max='100' style=""></progress> </body> </html> 

Javascript

  var es; function connect() { startListener(); $.ajax({ url: "server.php", success: function() { alert("Success"); }, error: function() { alert("Error"); } }); } function startListener() { es = new EventSource('monitor.php'); //a message is received es.addEventListener('message', function(e) { var result = JSON.parse(e.data); if (e.lastEventId == 'CLOSE') { alert("Finished!"); es.close(); } else { var pBar = document.getElementById('progressor'); pBar.value = result; } }); es.addEventListener('error', function(e) { alert('Error occurred'); es.close(); }); } function stopListener() { es.close(); alert('Interrupted'); } function addLog(message) { var r = document.getElementById('results'); r.innerHTML += message + '<br>'; r.scrollTop = r.scrollHeight; } 

PHP monitoring

 <?php SESSION_START(); header('Content-Type: text/event-stream'); // recommended to prevent caching of event data. header('Cache-Control: no-cache'); function send_message($id, $data) { $d = $data; if (!is_array($d)){ $d = array($d); } echo "id: $id" . PHP_EOL; echo "data: " . json_encode($d) . PHP_EOL; echo PHP_EOL; ob_flush(); flush(); } $run = true; $time = time(); $last = -10; while($run){ // Timeout kill checks if (time()-$time > 360){ file_put_contents("test.txt", "DEBUG: Timeout Kill", FILE_APPEND); $run = false; } // Only update if it changed if ($last != $_SESSION['progress']['percent']){ file_put_contents("test.txt", "DEBUG: Changed", FILE_APPEND); $p = $_SESSION['progress']['percent']; send_message(1, $p); $last = $p; } sleep(2); } ?> 

EDIT: I tried a different approach, where:

  • The AJAX page invokes page B, which launches the request, and saves the progress in the SESSION variable
  • The AJAX page calls page C every 2 seconds, which simply returns the value of the session variable. This cycle stops when it reaches 100

However, this also does not quite work. It seems that two AJAX requests or two server servers are not working at the same time.

Looking at the debug output: both AJAX calls are made at about the same time, but then the B script page starts by itself, and then the C script page starts. Is this some PHP restriction that I am missing?

more code!

Server (page B) PHP

 <?PHP SESSION_START(); file_put_contents("log.log", "Job Started\n", FILE_APPEND); $job = isset($_POST['job']) ? $_POST['job'] : 'err_unknown'; $_SESSION['progress']['job'] = $job; $_SESSION['progress']['percent'] = 0; $max = 10; for ($i=0; $i<=$max;$i++){ $_SESSION['progress']['percent'] = floor(($i/$max)*100); file_put_contents("log.log", "Progress now at " . floor(($i/$max)*100) . "\n", FILE_APPEND); sleep(2); } file_put_contents("log.log", "Job Finished", FILE_APPEND); echo json_encode("Success. We are done."); ?> 

Progress (Page C) PHP

 <?php SESSION_START(); file_put_contents("log.log", "PR: Request Made", FILE_APPEND); if (isset($_SESSION['progress'])){ echo json_encode(array("job"=>$_SESSION['progress']['job'],"progress"=>$_SESSION['progress']['percent'])); } else { echo json_encode(array("job"=>"","progress"=>"error")); } ?> 

Index (Page A) JS / HTML

 <!DOCTYPE html> <html> <head> <title>Progress Bar Test</title> </head> <body> <input type="button" value="Start Process" onclick="start('test', 'pg');"/><br /> <progress id="pg" max="100" value="0"/> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> <script type="text/javascript"> var progress = 0; var job = ""; function start(jobName, barName){ startProgress(jobName, barName); getData(jobName); } function getData(jobName){ console.log("Process Started"); $.ajax({ url: "server.php", data: {job: jobName}, method: "POST", cache: false, dataType: "JSON", timeout: 300, success: function(data){ console.log("SUCCESS: " + data) alert(data); }, error: function(xhr,status,err){ console.log("ERROR: " + err); alert("ERROR"); } }); } function startProgress(jobName, barName){ console.log("PG Process Started"); progressLoop(jobName, barName); } function progressLoop(jobName, barName){ console.log("Progress Called"); $.ajax({ url: "progress.php", cache: false, dataType: "JSON", success: function(data){ console.log("pSUCCESS: " . data); document.getElementById(barName).value = data.progress; if (data.progress < 100 && !isNaN(data.progress)){ setTimeout(progressLoop(jobName, barName), (1000*2)); } }, error: function(xhr,status,err){ console.log("pERROR: " + err); alert("PROGRESS ERROR"); } }); } </script> </body> </html> 

Debugging: log.log output

 PR: Request Made Job Started Progress now at 0 Progress now at 10 Progress now at 20 Progress now at 30 Progress now at 40 Progress now at 50 Progress now at 60 Progress now at 70 Progress now at 80 Progress now at 90 Progress now at 100 Job Finished PR: Request Made 
+7
javascript jquery ajax php server-sent-events
source share
1 answer

In such cases, I usually do it like this:

  • The client sends an AJAX request to page B. Important . If successful, the client sends the same request again.
  • The initial query on page B says: OK, THERE ARE 54555 RECORDS. . I use this account to run a progress bar.
  • In each of the following queries, page B returns a piece of data. The client counts the line size and updates the progress bar. He also collects pieces in one list.
  • At the last request, when all the data is sent, page B says: THAT ALL and the client displays the data.

I think you have an idea.

NOTE. You can request all the pieces in parallel, but this is a complicated way. The server (page B) should also return a fixed chunksize in the original response, and then the client sends TOTAL_COUNT / CHUNK_SIZE at the same time and combines the responses until the last request is completed. So it is much faster. You can use https://github.com/caolan/async in this case to make the code more readable.

+2
source share

All Articles