如何让PHP更新javascript的冗长进程状态


How to get PHP to update javascript of status of lengthy process?

我有下面的Angular网站,它显示Angular可以更新页面,而PHP可以在后台通过AJAX执行一些过程。

然而,我现在如何获得我的PHP进程,例如for循环,以传递回$x的值,这样我就可以在$x递增时显示它的后面?

index.php

<!doctype html>
<html lang="en">
    <head>
      <meta charset="utf-8">
      <title>ajaxtest</title>
      <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
    </head>
    <body ng-app="mainApp">
        <div ng-controller="mainController">
            <div>Angular is counting: {{counter}}</div>
            <button ng-click="processFiles()">processFiles</button>
            <div>{{message}}</div>
        </div>
        <script>
        var mainApp = angular.module('mainApp',[]);
        function mainController($scope, $interval, $http) {
            var theTimer = $interval(function () {
                $scope.counter++;
            }, 1000);           
            $scope.counter = 0;
            $scope.message = 'click the button to have PHP do something in the background while Angular should continue to count';
            $scope.processFiles = function() {
                $scope.message = 'processing...';
                $http.get('http://localhost/webs/ajaxtest/data.php', {
                    params: {
                        taskIdCode: 'getLoadingStatus'
                    }
                }).success(function (data) {
                    $scope.message = data['status'];
                }); 
            }
        }
        </script>
    </body>
</html>

data.php

        $taskIdCode = filter_input(INPUT_GET, 'taskIdCode', FILTER_UNSAFE_RAW);
        $methodName = 'task_' . $taskIdCode;
        $this->$methodName();
    }
    public function task_getLoadingStatus() {
        for($x = 1; $x <= 4; $x++) {
            sleep(1);
        }
        $data['status'] = 'finished';
        echo json_encode($data);
    }
}
$taskRunner = new TaskRunner();

正如Ricardo所说,一种选择是通过调用处理的同一通道反馈状态消息-这在AJAX请求上在技术上是可行的,但需要对Web服务器和任何中间组件进行大量修改,以将内容刷新回Javascript,并需要对Javascript进行复杂处理以处理部分响应。这就是websocket要解决的问题。通过HTTP请求处理长时间运行的进程不是一种好的做法。

对这种方法的进一步考虑是,HTTP及其基础设施是围绕服务器对请求做出快速响应的概念设计的。

如果你不走这条路,那么你需要通过另一个渠道提供数据——显而易见的解决方案是AJAX Web服务,它会及时响应当前状态。

但由于这与调用无关,因此它需要一种机制来引用正在监视的特定进程。如果你已经有了一个会话,那么你可以使用会话id或会话本身作为密钥——然而,如果会话与多个这样的进程相关联,这是行不通的。此外,通过javascript公开会话id对安全性有很大影响。

或者,您可以用javascript创建一个(希望是唯一的)密钥,并将该密钥注入到流程调用中。

            var taskKeyValue=generate_unique_id();
            var keepChecking;
            // first invoke processing.....
            $scope.processFiles = function() {
            $scope.message = 'processing...';
            $http.get('http://localhost/webs/ajaxtest/data.php', {
                params: {
                    taskKey: taskKeyValue
                }
            }).success(function (data) {
                $scope.message = data['status'];
                clearInterval(keepChecking);
            }); 
            // then while this is outstanding
            var checkFiles = function() {
            $http.get('http://localhost/webs/ajaxtest/pollStatus.php', {
                params: {
                    taskKey: taskKeyValue
                }
            }).success(function (data) {
                $scope.message = data['status'];
            }); 
            keepChecking=set_interval(checkFiles, 2000);

然后在您现有的php代码中。。。。

 function doing_slow_thing() {
    for($x = 1; $x <= 40; $x++) {
        file_put_contents('/tmp/' . basename($_GET['taskKey']), $x);
        sleep(1);
    }
    $data['status'] = 'finished';
    echo json_encode($data);
 }

和pollStatus.pp:

<?php
    print json_encode(
          array('status'
             =>file_get_contents('/tmp/' . basename($_GET['taskKey']))
          ));

如果您不使用Websockets(或某种类型的服务器推送),您将不得不采用长轮询。

我的建议是在你的控制器中如下:

var promise = null;
var poller = function() {
    console.log("Query the server for the amount of progress done using another $http.get");
};

在执行$http.get以启动长操作之前的功能中:

$scope.processFiles = function() {
  $scope.message = 'processing...';
  //
  // Initialize the interval pooling operation
  //
  var promise = $interval(poller, 1000);
  $http.get('http://localhost/webs/ajaxtest/data.php', {
    params: { 
      taskIdCode: 'getLoadingStatus'
    }
  }).success(function (data) {
    //
    // Terminate the interval so it does not run forever!
    //
    $interval.cancel(promise);
    $scope.message = data['status'];
  }).error(function() {
    $interval.cancel(promise);
  }); 
}

然后,在服务器端,您必须将进度存储在数据库中(或您可能拥有的任何等效内容),以便由轮询操作进行检查。在sleep之后,每个进度步骤都必须更新此状态。

希望这些信息能让你开始:)