What is the best way to show progress in an Ajax call?

I have an Ajax call that updates 5,000 records in a database, so this takes a lot of time. I have an Ajax “Image Download” showing that something is happening, but I am looking for the best way to show “Update 50 of 5000 ...”, “Update 200 of 5000” or something like that.

What is the best way to do something like this in Ajax / jQuery without doing 5,000 different posts?

+33
jquery ajax progress-bar
Oct 10 2018-10-10
source share
12 answers

The best I think is using Comet .

In applications such as Comet, the server can essentially transmit data to the client (instead of client request data from the server again and again). The client should only connect to the server once. and then the server will continue to return data to the client.

Material from Wikipedia:

Comet is a programming method that allows web servers to send data to a client without requiring a request from a client. This allows you to create event-driven web applications hosted in a browser.

Now let's see how the comet works. See the following server code. the while is used here, instead you can set your own condition. In a while loop, the page records the date and time, and then sleeps for 1/2 seconds.

ASP.NET Page Code: Service.aspx.cs

 public static string Delimiter = "|"; protected void Page_Load(object sender, EventArgs e) { Response.Buffer = false; while (true) { Response.Write(Delimiter + DateTime.Now.ToString("HH:mm:ss.FFF")); Response.Flush(); // Suspend the thread for 1/2 a second System.Threading.Thread.Sleep(500); } // Yes I know we'll never get here, // it just hard not to include it! Response.End(); } 

Client Code - Pure JavaScript

Make a request only once, and then continue to validate the data in readyState === 3 XMLHttpRequest .

 function getData() { loadXMLDoc("Service.aspx"); } var req = false; function createRequest() { req = new XMLHttpRequest(); // http://msdn.microsoft.com/en-us/library/ms535874%28v=vs.85%29.aspx } function loadXMLDoc(url) { try { if (req) { req.abort(); req = false; } createRequest(); if (req) { req.onreadystatechange = processReqChange; req.open("GET", url, true); req.send(""); } else { alert('unable to create request'); } } catch (e) { alert(e.message); } } function processReqChange() { if (req.readyState == 3) { try { ProcessInput(req.responseText); // At some (artibrary) length recycle the connection if (req.responseText.length > 3000) { lastDelimiterPosition = -1; getData(); } } catch (e) { alert(e.message); } } } var lastDelimiterPosition = -1; function ProcessInput(input) { // Make a copy of the input var text = input; // Search for the last instance of the delimiter var nextDelimiter = text.indexOf('|', lastDelimiterPosition + 1); if (nextDelimiter != -1) { // Pull out the latest message var timeStamp = text.substring(nextDelimiter + 1); if (timeStamp.length > 0) { lastDelimiterPosition = nextDelimiter; document.getElementById('outputZone').innerHTML = timeStamp; } } } window.onload = function () { getData(); }; 

Link

+23
Dec 20 '10 at 8:55
source share
— -

I would allow a function that makes a large update record in the SESSION variable, its current progress after each separate (or so many) update, and uses a separate AJAX script to extract this progress value from SESSION and let JavaScript use this to update the progress bar / text .

+4
Oct 12 2018-10-12
source share

I assume that you use one POST for all entries in the batch update and place the upload image between the call and the return.

Instead of waiting for the server to return before the update is complete, immediately return it using the special identifier for this batch update. Then make a server call that returns the status of the batch update, which your progress dialog may call to report the progress.

 var progressCallback = function(data){ //update progress dialog with data //if data does not indicate completion //ajax call status function with "progressCallback" as callback }); //ajax call status function with "progressCallback" as callback 
+2
Oct. 10 2018-10-10
source share

I would like to start an Ajax callback every milliseconds, which can ask how much has been done (for example, the number of updated records), and use this to display a progress bar. Something like the way this works.

+2
Dec 14 '10 at 5:08
source share

You can update the response buffer with progress by periodically flushing the response buffer from the server.

But you may have a problem reading the request before completing it through xhttpr . Perhaps you can make your request through an iframe and perform this download through an "HTTP stream".

But even this can be sketchy. HTTP is not intended to transfer things piecemeal / fragmented. Like others, it is best to make separate subsequent calls to get the status of an operation.

+2
Dec 14 '10 at 5:19
source share

I got an idea when reading the answers.

Cookies for JavaScript and PHP,

  • Create a cookie with JavaScript when calling Ajax.
  • In the PHP Ajax file, increase the value in this cookie with each SQL update.
  • In JavaScript, a recursive function will read this particular cookie and will update progress bar numbers.

Benefits:

  • Only 1 Ajax call.
  • Less load on the server.
+2
Dec 15 '10 at 12:18
source share

I assume that you have a reason for repeating each record separately, rather than simply executing an SQL statement.

If so, just make an ajax call every 200 or so iterations. If you do this for each group of 200 entries, it will only consume 50 Ajax calls.

Something like (pseudo code):

 If iterationNumber mod 200 == 0 // Make Ajax call. 
+1
Oct 10 2018-10-10
source share

I'm not sure which server side you are sending messages to, but you should use this method for most programming languages. I am using PHP as an example.

On the HTML side, there is a function that updates the progress bar to a given width. I call this setProgress function for this example, which takes a number to update the progress bar.

In the server-side code, make update chunks (say 100 per iteration) and generate the output. Output javascript request for each iteration:

 <?php while () { // Whatever your loop needs to be. // Do your chunk of updates here. ?> <script type="text/javascript"> setProgress(100); </script> <?php flush(); // Send what we have so far to the browser so the script is executed. } setProgress(5000); // All done! ?> 

After the echo, discard the output buffer to ensure that this data is sent to the browser. Since this is a full script tag, the browser will execute javascript internally, updating the progress bar to whatever value you pass to it.

For this to work, you will need to add some calculations to understand the numbers that you call on the progress bar, but I assume this should not be too much of a problem. It probably makes sense to have setProgress in the percent usage example, although I would like to avoid the calculations needed for clarity.

+1
Dec 17 '10 at 21:50
source share

To show the progress during loading, I would modify my backend so that it can perform selective loading.

For example,

 var total_rec = 5000; var load_steps = 20; var per_load = total_rev / load_steps; var loaded = 0; while (loaded < total_rec) { www.foobar.com/api.php?start=loaded&end=(loaded+per_load); loaded += per_load; } 

Each time a download is in progress, update the progress bar.

An alternative way to change the backend may be

 www.foobar.com/api.php?start=loaded&count=50 
0
Dec 14 '10 at 5:29
source share

Something seems suspicious.

I am not familiar with databases that cannot update 5000 records in flash ... So, is this not only the one update you apply, but also the record using record update?

Consider a system that allows a user to download 5,000 records and not indicate which ones have been edited, and then at the end of creating updates, one update button is applied, which would require all 5,000 records to be transferred in some way. This will be the worst case.

So there may be some way to split the problem in such a way that there is no timeout. Consider, for example, a temporary db table (or just in the application memory, quite simply by simply creating a list of ORM objects ... but that's aloof), then when it comes time to commit these updates, they can at least be made in without the need for client-server transfers. It may even be possible to mark individual fields that have been edited, so nothing is updated in the database except what has changed.

There are lengthy processes, and I know that sometimes you are stuck to use what someone has provided you ... but maybe with a little thought you can just get rid of the wait time.

0
Dec 15 '10 at 19:39
source share

Create a simple table as follows:

 CREATE TABLE progress_data ( statusId int(4) NOT NULL AUTO_INCREMENT, progress float DEFAULT NULL COMMENT 'percentage', PRIMARY KEY (id_progress_data) ); 

JQuery code:

 //this uses Jquery Timers http://plugins.jquery.com/project/timers $('#bUpdate').click(function() { //first obtain a unique ID of this operation - this has to by synchronized $.ajaxSetup({'async': false}); $.post('ajax.php', {'operation': 'beginOperation'}, function(data) { statusId = parseInt(data.statusId); }); //now run the long-running task with the operation ID and other params as necessary $.ajaxSetup({'async': true}); $.post('ajax.php', {'operation': 'updateSite', 'statusId': statusId, 'param': paramValue}, function(data) { $('#progress_bar').stopTime('statusLog'); //long operation is finished - stop the timer if (data.result) { //operation probably successful } else { //operation failed } }); //query for progress every 4s, 'statusLog' is just the name of the timer $('#progress_bar').everyTime('4s', 'statusLog', function() { var elm = $(this); $.post('ajax.php', {'operation': 'showLog', 'statusId': statusId}, function(data) { if (data) { //set bar percentage $('#progress').css('width', parseInt(data.progress) + '%'); } }); }); return false; } 

Internal code (in PHP):

 if (isset($_POST['operation'])) { ini_set("display_errors", false); session_write_close(); //otherwise requests would block each other switch ($_POST['operation']) { /** * Initialize progress operation, acquire ID (statusId) of that operation and pass it back to * JS frontend. The frontend then sends the statusId back to get current state of progress of * a given operation. */ case 'beginOperation': { $statusId = //insert into progress_data echo json_encode(array('statusId' => $statusId)); break; } /** * Return back current progress state. */ case 'showLog': { $result->progress = (float) //SELECT progress FROM progress_data WHERE statusId = $_POST['statusId'] echo json_encode($result); break; } case 'updateSite': { //start long running operation, return whatever you want to, during the operation ocassionally do: UPDATE progress_data SET progress=... WHERE statusId = $_POST['statusId'] } } } /* Terminate script, since this 'view' has no template, there si nothing to display. */ exit; 

I have already used this approach in 3 applications, and I have to say that it is very reliable and fast enogh (the showLog operation is just a simple SELECT statement). You can also use the session to save progress, but this creates a lot of problems, since the session must be closed for writing (if it is stored in files), otherwise showLog AJAX requests will wait for the long operation to complete (and free sense).

0
Dec 19 '10 at 23:00
source share

I once did something similar (similar to Zain Shaikh , but simpler):

On server

 int toUpdate = 5000; int updated = 0; int prev = updated; while(updated < toUpdate) { updated = getAlreadyUpdatedRows(); flushToClient(generateZeroSequenceOfLength(updated - prev)); prev = updated; sleep(500); } closeStream(); 

On the client
Follow the path of Zain Shaikh , but on ProcessInput just resize the ProcessInput bar according to the ratio of the number of records to update and input.length .

This solution often handles client-side complexity for network bandwidth.

Do not mix the activity of this server with the final input of the file if you really do not know what you are doing. You will trade db requests (the choice for counting updated lines) for stability: what if the user changes the page? There is no problem for the progress bar, but the import will not be completed.

0
Dec 21 2018-10-12T00:
source share



All Articles