Progress Indicator for an AJAX Request?


#1

I'm creating an app that sends an AJAX request to a PHP page. I'd like to have a progress bar for the time it takes to complete the request. Note that the PHP page creates a file and writes to it within some sort of loop.

Can this be done? So far, I've not found a single reliable source that...

1.) Clearly explains any and all steps required.
2.) Works.
3.) Uses jQuery. (I'm not opposed to fundamental JavaScript at all but I'd prefer jQuery since the rest of the app uses it already.)

From the research I've done up to this point, it seems that the success of the response is contingent upon whether the content size is returned... Is this true? I've found that when the PHP page creates a file, the response usually trips the error condition in the AJAX call, but I'm not sure why...

Here's the PHP I'm toying with (test.php):

<?php

    file_put_contents('file.tmp', '', FILE_APPEND);

    ob_start();

    for($i=0;$i<10000;$i++){
        var_dump($i);
        file_put_contents('file.tmp', $i."\n", FILE_APPEND);
    }

    rename('file.tmp', 'final.txt');

    $length = ob_get_length();
    header('Content-Length: '.$length."\r\n");
    header('Accept-Ranges: bytes'."\r\n");
    ob_end_flush(); 
?>

Long story short, the AJAX sends the request over to that file and the intended purpose of it is to create a temporary file that gets written to in the FOR loop... After everything is written, the temporary file is renamed to "final.txt" whereby the content length is supposed to be used in the header call before flushing the buffer.

Now, the jQuery I'm using is as follows:

    $('#test').click(function(e){
        $.ajax({
            method: 'GET',
            url: 'test.php',
            dataType: 'json',
            success: function() { },
            error: function() { },
            progress: function(e) {
                //make sure we can compute the length
                console.log(e);
                if(e.lengthComputable) {
                    //calculate the percentage loaded
                    var pct = (e.loaded / e.total) * 100;

                    //log percentage loaded
                    //console.log(pct);
                    $('#progress').html(pct.toPrecision(3) + '%');
                }
                //this usually happens when Content-Length isn't set
                else {
                    console.warn('Content Length not reported!');
                }
            }
        });

    });

When I execute this, it does basically work but the percentage indication (inside of #progress) isn't. Instead of seeing a gradual calculation of the percentage values, it instead either doesn't show or else suddenly shows 100%. It works better when a file write isn't occurring in test.php, but since I need test.php to write files, well, I need to figure out what's happening with the AJAX request...

Any ideas what's going on here? Maybe an all-around better way exists? If so, I'd love to know about it. smile

(Mods: feel free to move this if necessary...)


#2

I doubt this will be possible (if you take that verbatim) … for that you would need to know how long your request takes at the very beginning of your request.

however, what you can monitor is the upload progress (since received/sent data vs. total file size is determinable) or maybe some write progress on the server (again comparing the amount of data processed)


#3

Thanks for the response, Dormilich.

My understanding of things like this is somewhat limited but I think I might be heading in the right direction with what I have below (I found this here):

index.php:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <link rel="stylesheet" type="text/css" media="all" href="jquery_ui.css" />
        <style type="text/css">
            .control{width:200px}
            #execute{border:1px solid #000;width:100%;background:none;cursor:pointer}
            #progressbar{border:none !important;height:3px !important;text-align:center !important}
            .ui-progressbar-value{margin-right:auto !important;margin-left:auto !important}
            .ui-widget-header{background:red !important}
        </style>
    </head>

    <body>
        <h1>Processing Bar</h1>
        <div class="control">
            <input id="user_number" type="text" value="" />
            <button type="button" id="execute">Execute</button>           
            <div id="progressbar"></div>
        </div>
        <div id="message"></div>
        <script type="text/javascript" src="jquery.js"></script>
        <script type="text/javascript" src="jquery_ui.js"></script>

        <script type="text/javascript">
            $('#execute').click(function(){
                var dataString = "user_number="+$('#user_number').val();

                $('#message').hide();

                $.ajax({
                    url : "test.php",
                    type: "GET",
                    data: dataString,
                    datatype:"json",
                    complete:function(){
                        $("#progressbar").progressbar({
                            value:100
                        });

                        $('#message').html('Complete');
                        $('#message').show();
                    }
                });

                /*
                    call the updateStatus() function every 3 second to update progress bar value.
                */
                t = setTimeout("updateStatus()", 3000);
            });



            /*
                .getJSON will get the json string being updated by the server.php in server. every 3 second, the 'total' and 'current' count will be parsed and updated to the progress bar.
            */
            function updateStatus(){      
                $.getJSON('status.json', function(data){ 
                    var items = [];
                    pbvalue = 0;
                    if(data){
                        var total = data['total'];
                        var current = data['current']; 
                        var pbvalue = Math.floor((current / total) * 100); 
                        if(pbvalue>0){ 
                            $("#progressbar").progressbar({
                                value:pbvalue
                                //$('.amount').css('width',pbvalue);
                            }); 
                        } 
                    } 

                    if(pbvalue < 100){ 
                        t = setTimeout("updateStatus()", 1000); 
                    } 
                });   
            } 
        </script>

    </body>
</html>

test.php:

 //set a default number if none exist already...
        $num = 10000;

        //file_put_contents('log.tmp', $_REQUEST['user_number'], FILE_APPEND);

        if(isset($_REQUEST['user_number']) && $_REQUEST['user_number'] !== '')
            $num = $_REQUEST['user_number'];

        $filename = 'status1.json';
        $fp = fopen($filename, "w"); 
        $arr = array('total'=>'0', 'current'=>'0'); 

        fwrite($fp, json_encode($arr)); 

        fclose($fp); 

        $arr['total'] = $num; 

        for($i = 1; $i < $num+1; $i++){ 
            //here you can do some other work as well.  

            for($x=0;$x<1000;$x++){
                var_dump($x);
            }

            $arr['current'] = "$i";   
            $fp = fopen($filename, "w"); 
            fwrite($fp, utf8_encode(json_encode($arr))); 
            fclose($fp); 
            copy('status1.json', 'status.json'); 
        }

The bad thing about this tutorial code is that the response is contingent upon the number that the user submits in the text box. Because of this, it's not really a true AJAX "stopwatch." Ideally, I'd like to be able to simply execute an AJAX request against any PHP page I deem necessary and have the progress bar measure how long it takes to finish the execution. I've come close to having this by using the content-length header in the PHP pages, but it seemed to only work when messing with text-only data (i.e. - when I tried to use logic in the PHP page to write data to files, it would fault and return error messages in Firebug about the content / data not being text; additionally, the progress bar wasn't always honest, too, or else wouldn't work at all).

This is a very tricky issue indeed--I haven't been able to find any silver bullets with this but I'd like to think that a way exists to somehow use AJAX to submit a request to a PHP page whereby a progress bar then reflects the time it takes for the PHP page to complete the logic it performs... (How might something like this be done if the only thing being done in the PHP page is either writing files or else processing / parsing data? How would one compute the amount of memory needed to be used as a metric for calculating progress? If that could be done, it would make the entire approach doable, right?)


#4

from my experience using an upload progress bar, I basically hooked into the JS progress event. no server-side code was involved for that.


#5

Aren't most of the indicators some type of show spinner gif, when response returns hide spinner gif?
i.e. not a "real" progress indicator, but more a "in progress" indicator.


#6

that would be the easy solution when your browser doesn’t support the progress event.


#7

Thanks

Except for Opera-Mini using it looks like a pretty safe bet.
http://caniuse.com/#search=progress


#8

and IE (no support until IE10 and we know that IEs are not updated as much as other browsers)


#9

I found this.

Judging from what's been said so far, it sounds like that link should be right up the alley of what you guys are talking about... I need the stuff in the PHP page that I'm submitting the request to, so I'm thinking that I could replace the upload logic / approach with a basic request to the PHP page (only using the progress event). Does this sound right?


#10

hm, the progress event only tracks the data being uploaded to the server and since your upload size is something around 20 Bytes it should be uploaded in visually an instant. how long the server takes for the response cannot be known by a single AJAX request.

Of course you could open a Socket connection to receive status messages about progress from the server, but again looking at your PHP code I doubt it will run longer than a second, so it doesn’t make sense to track the processing status there as well.

note: for comparison I have used an upload progress bar on a ~200 KB File, that took about a second to complete.


#11

The issue though is that I intend to use this progress approach with bigger processing jobs. So that 20B thing you mentioned is likely just proof-of-concept code. Does this change things?

I'm also curious about what you would deem as being "progress bar appropriate" processing sizes... When I reset the FOR loop in that PHP code above to have an iteration length of about 10,000+, it began to take long enough to merit the use of a progress bar. I'm not sure how you're getting the 20B size with that in mind but it's probably because I didn't explain that or make it clear enough to be understood--my apologies.


#13