我正在尝试制作一个页面,该页面将通过复杂的数据库查询和php解析生成结果集...但这主要是要点...要点是,这需要一两分钟才能完成,并且我希望显示一个进度条,而不是普通的gif动画“正在加载...”图片。

故障将是...

  • 用户打开页面A。
  • 页面A向页面B请求数据(很可能是AJAX)。
  • 页面B处理数据库中的100000多个左右的条目并进行解析。
  • 页面A显示了一个进度条,它大致显示
  • 在整个过程中的距离
  • 页面B返回结果集。
  • 页面A显示结果集。

  • 我知道如何将数据返回给ajax查询,但是我的问题是我不知道如何连续返回数据以显示进程的状态(例如,扫描的行数百分比)。

    我已经研究了EventSource/Server-Sent-Events,它显示了 promise ,我只是不太确定如何使其正常工作,或者是否有更好的方法来做到这一点。

    我尝试制作一个快速的小模型页面,仅使用EventSource可以正常工作,但是当我将其拆分为一个eventSource调用(该页面监视 session 变量的更改)和ajax请求(实际数据发送/返回)它分崩离析。

    我可能会丢失一些明显的东西,或者做一些愚蠢的错误,但这仍然是我所拥有的大部分东西。任何帮助,建议,技巧甚至完全其他方法的建议都将是很棒的:)

    用户页面:
    <!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>
    

    Java脚本
     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
    <?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's 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);
    }
    ?>
    

    编辑:
    我尝试了一种不同的方法,其中:
  • 页面A AJAX调用页面B,后者运行请求,并将进度保存到SESSION变量
  • 页面A AJAX每2秒调用一次页面C,它仅返回 session 变量的值。当达到100
  • 时,此循环终止

    但是,这也不是很有效。似乎两个AJAX请求或服务器端的两个脚本未同时运行。

    查看调试输出:两个AJAX调用大约在同一时间执行,但是B页脚本自身运行完毕,然后-C页脚本运行。这是我缺少的PHP的某些限制吗???

    更多代码!

    服务器(页面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.");
    ?>
    

    进度(第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"));
        }
    ?>
    

    索引(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>
    

    调试:log.log输出
    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
    

    最佳答案

    在类似的情况下,我通常是这样进行的:

  • 客户端向页面B发送AJAX请求。
    重要:成功后,客户端再次发送相同的请求。
  • 在最初的请求中,页面B表示:OK, THERE ARE 54555 RECORDS.。我使用此计数来启动进度栏。
  • 在每个下一个请求上,页面B返回一块数据。客户端计算块的大小并更新进度条。它还在一个列表中收集块。
  • 在最后一个请求中,当所有数据发送完毕后,页面B会说:THAT'S ALL,客户端将呈现数据。

  • 我认为,您已经明白了。

    注意:您可以并行请求所有块,但这是一种复杂的方法。服务器(页面B)还应在初始响应中返回固定的chunksize,然后客户端同时发送TOTAL_COUNT / CHUNK_SIZE请求并合并响应,直到最后一个请求完成为止。因此它要快得多。在这种情况下,可以使用https://github.com/caolan/async使代码更具可读性。

    09-25 16:35
    查看更多