【问题标题】:How to get PHP to update javascript of status of lengthy process?如何让 PHP 更新冗长进程状态的 javascript?
【发布时间】:2015-10-29 11:33:19
【问题描述】:

我有以下 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();

【问题讨论】:

  • 使用 websockets 或 javascript 长轮询

标签: javascript php angularjs


【解决方案1】:

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

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

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

但是由于这与调用是分离的,它需要一种机制来引用被监视的特定进程。如果您已经有一个会话,那么您可以使用会话 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.php 中:

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

【讨论】:

  • 其实我的答案和你的目标一样。轮询函数将对另一个提供当前状态的脚本执行$http.get。其目的不是推动部分回应或做一些不寻常的事情。稍后我将查看我的答案,看看我可以改进的地方:) 另外,作为对您自己答案的改进,您应该使用 AngularJS 的$interval 服务。尽管它主要是 setInterval 的包装器,但它更适合与 AngularJS 一起使用。
  • @Piskvor:即使使用默认(阻塞)会话处理程序,您也可以随时在代码中关闭(并因此解锁)会话。见 session_write_close()
【解决方案2】:

如果您不使用 Websockets(或某种类型的服务器推送),您将不得不求助于 long-polling

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

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 之后的每个进度步骤,您都必须更新此状态。

希望这些信息可以帮助您入门 :)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-10
    • 2015-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-26
    相关资源
    最近更新 更多