What is the best way of showing progress on an Ajax call?

前端 未结 12 1007
遥遥无期
遥遥无期 2020-11-28 05:17

I have an Ajax call that updates 5,000 records in a database so this takes a lot of time. I have an Ajax \"Loading image\" showing that something is happening, but I am loo

相关标签:
12条回答
  • 2020-11-28 05:34

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

    On the 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 Zain Shaikh path but on ProcessInput simply resize your progress bar according to the ratio between the number of records to update and the input.length.

    This solution often trades client side complexity for network bandwidth.

    Do not mix this server activity with an eventual file import, unless you really know what you are doing. You would be trading db queries (the select for counting the updated rows) for stability: what if the user change page? There's no problem for the progress bar, but the import would not be completed.

    0 讨论(0)
  • 2020-11-28 05:35

    You could update the response buffer with a progress, flushing your response buffer periodically from the server.

    But you may have trouble reading a request before it's complete via xhttpr. You might be able to make your request via an iframe, and have that load in progress via 'http streaming'.

    But even that can be sketchy. HTTP is not the meant to transfer things piecemeal/fragmented. Like others point out, it would be best to make a separate subsequent calls to get the status of the operation.

    0 讨论(0)
  • 2020-11-28 05:38

    I am assuming that you have a reason for iterating through each record individually, instead of simply running a SQL statement.

    If that is the case, simply make an ajax call every 200 or so iterations. If you do it for each group of 200 records, it will only consume 50 Ajax calls.

    Something like (pseudocode):

    If iterationNumber mod 200 == 0
        // Make Ajax call.
    
    0 讨论(0)
  • 2020-11-28 05:40

    I would fire an Ajax callback once every n milliseconds that can query on how much is done (e.g. number of records updated) and use that to display a progress bar. Something like the way this works.

    0 讨论(0)
  • 2020-11-28 05:43

    Create a simple table like this:

    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;
    }
    

    Backend 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 used this approach in 3 applications already and I must say it is very reliable and fast enogh (the showLog operation is just a simple SELECT statement). It is also possible to use session to store the progress, but that brings a lot of problems, since the session has to be write closed (if it is stored in files), otherwise the showLog AJAX queries will wait for the long operation to finish (and loose sense).

    0 讨论(0)
  • 2020-11-28 05:47

    I'm not sure what the server-side is where you are posting to, but you should be able to apply this method to most programming languages. I'm using PHP as an example.

    On the HTML-side, have some function that updates the progress bar to a given width. I'm calling this function 'setProgress' for this example, which takes a number to update the progress bar to.

    In the server-side code, do chunks of updates (say 100 per iteration) and generate output. By outputting a javascript call 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 echoing this, flush the output buffer to make sure this data is sent to the browser. Because it is a complete script tag, the browser will execute the javascript inside, updating the progress bar to whatever value you passed to it.

    To get the whole thing working you will need to add some calculations to make sense of the numbers you are calling to the progress bar, but I'm assuming that shouldn't be too much of a problem. It would probably make more sense to have setProgress in the example use percentages, though I wanted to stay clear of the calculations needed for the sake of clarity.

    0 讨论(0)
提交回复
热议问题