如何收听异步服务器端 Java 控制器?

How to listen to async server side Java Controller?

谷歌搜索我遇到了很多例子,其中一个是来自 Angular 的 $q,以避免递归地访问我的服务器端来检查请求是否包含数据。

我看到这个 link :

建议使用 $q,但我认为这不会解决我的问题。

JS:

function OrderFormController($scope, $http, $q) {

    $('#loaderImage').show();

    $http.get('/utilities/longProcess')
        .success(function(data, status, headers, config) {
            if(data != 'Still Processing'){
                console.log('Data from server');
                $scope.sampleJSON = data.pmdStructureWrapper;
                $scope.sampleJSONDuplicates = data.pmdDuplicates;
                $scope.$watch('sampleJSON', setTimeout(function() {
                    $('.panel-body li').each(function() {
                        if ($.trim($(this).text()) === "") {
                            $(this).hide();
                        }
                    });
                }, 1000));
            $('#loaderImage').hide();
            }else{
                console.log('rejected');
                $q.reject(data);
            }
        })
        .error(function(data, status, header, config) {

        });
}

控制器:

@RequestMapping("/utilities/longProcess")
public CompletableFuture<String> asyncLongProcess(HttpServletResponse response, HttpServletRequest request) {
    HttpSession session = request.getSession();
    return CompletableFuture.supplyAsync(() -> session.getAttribute("CACHED_RESULT"))
            .thenComposeAsync(obj -> {
                if (obj == null) {
                    if(session.getAttribute("BACKGROUND_PROCESSING") == null) {
                        session.setAttribute("BACKGROUND_PROCESSING", true);
                        CompletableFuture.supplyAsync(() -> callURL(response, request))
                                .thenAccept(result -> session.setAttribute("CACHED_RESULT", result));
                    }
                    return CompletableFuture.completedFuture("Still Processing");
                }

                return CompletableFuture.completedFuture(obj.toString());
            });
}

HTML :

<body ng-app ng-controller="OrderFormController">
<header>
    <div class="container">
        <div id="branding">
            <h1><span class="highlight">Review  </span> Result</h1>
            <form class="form-logout" role="form" action="/logout">
                <input type="submit" value="Logout" id="logout" class="btn btn-primary btn-lg pull-right">
            </form>
        </div>

    </div>
</header>

<img src="../img/spinner.gif" id="loaderImage" style='display:none'>
<div class="col-md-12 col-lg-12" ng-cloak="">
    <div class="panel with-nav-tabs panel-default">
        <div class="panel-heading">
            <ul class="nav nav-tabs">
                <li class="active"><a href="#tab1default" data-toggle="tab">Classes</a></li>
                <li><a href="#tab2default" data-toggle="tab">Triggers</a></li>
                <li><a href="#tab3default" data-toggle="tab">Visualforce pages</a></li>
                <li><a href="#tab4default" data-toggle="tab">Duplicate Codes</a></li>
            </ul>
        </div>
        <div class="panel-body">
            <div class="tab-content">
                <div class="tab-pane fade in active" id="tab1default">
                    <ul class="col-md-12 col-lg-12">
                        <li ng-click="showErrorDetails(key)" class="col-sm-12 col-md-4 col-lg-4 eachClassCell"
                            ng-repeat='(key,value) in sampleJSON'>
                            <div ng-if="key.indexOf('.cls') > -1">
                                <div title="{{key}}" class="classNameLabel">{{key}}</div>
                                <div title="Error count" class="errorContainer">
                                    <span class="errorCount">{{value.pmdStructures.length}}</span>
                                    <span class="errorMeter"
                                          ng-repeat="eachClass in value.pmdStructures | limitTo: 10"></span>
                                </div>
                            </div>
                        </li>
                    </ul>
                </div>
                <div class="tab-pane fade" id="tab2default">
                    <ul class="col-md-12 col-lg-12">
                        <li ng-click="showErrorDetails(key)" class="col-sm-12 col-md-4 col-lg-4 eachClassCell"
                            ng-repeat='(key,value) in sampleJSON'>
                            <div ng-if="key.indexOf('.trigger') > -1">
                                <div title="{{key}}" class="classNameLabel">{{key}}</div>
                                <div title="Error count" class="errorContainer">
                                    <span class="errorCount">{{value.pmdStructures.length}}</span>
                                    <span class="errorMeter"
                                          ng-repeat="eachClass in value.pmdStructures | limitTo: 10"></span>
                                </div>
                            </div>
                        </li>
                    </ul>
                </div>
                <div class="tab-pane fade" id="tab3default">
                    <ul class="col-md-12 col-lg-12">
                        <li ng-click="showErrorDetails(key)" class="col-sm-12 col-md-4 col-lg-4 eachClassCell"
                            ng-repeat='(key,value) in sampleJSON'>
                            <div ng-if="key.indexOf('.page') > -1">
                                <div title="{{key}}" class="classNameLabel">{{key}}</div>
                                <div title="Error count" class="errorContainer">
                                    <span class="errorCount">{{value.pmdStructures.length}}</span>
                                    <span class="errorMeter"
                                          ng-repeat="eachClass in value.pmdStructures | limitTo: 10"></span>
                                </div>
                            </div>
                        </li>
                    </ul>
                </div>
                <div class="tab-pane fade" id="tab4default">
                    <ul class="col-md-12 col-lg-12">
                        <li ng-click="showDuplicateDetails(eachValue)" class="col-sm-12 col-md-4 col-lg-4 eachClassCell"
                            ng-repeat='eachValue in sampleJSONDuplicates'>
                            <div title="{{eachValue.duplicationInFile}}" class="classNameLabel">
                                {{eachValue.duplicationInFile}}
                            </div>
                        </li>
                    </ul>
                </div>
            </div>
        </div>
    </div>
</div>
<form>


</form>
<footer>
    <p>Salesforce Free Code Review, Copyright &copy; 2017</p>
    </p>
</footer>
<script type="application/javascript">


</script>
</body>

这样做是为了避免超时,它向客户端发送一个Still Processing消息,因为超时限制是30000ms, 我知道首先想到的是每个请求使用大约 30 个数据进行延迟加载,但相信我,我做不到。

我已经构建了一个代码审查工具,所以当 URL 被命中时,它会使用后台线程对所有 类 运行代码审查,然后 returns 响应在 CompletableFuture 大约 120 - 150 秒后。在页面再次刷新之前,客户端不知道此响应。

即使刷新页面后,由于数据超过 7MB,页面也需要将近 13-35 秒才能显示结果。

该代码在我的本地运行良好,但是当我托管在共享服务器 heroku 中时,我开始出现超时。 有两个问题:

1) angularJS 如何检测 RestAPI 响应的变化?(因为将发送两个响应,第一个是 "Still Processing" 上的即时响应,第二个是真实结果。这是一个我必须消费)。

2) 如何从Controller中延迟加载JSON格式的数据?

更新 1:

我也试过使用 OutputStream:

控制器:

@RequestMapping("/utilities/longProcessStream")
public StreamingResponseBody asyncLongProcessStream(HttpServletResponse response, HttpServletRequest request) {
    return new StreamingResponseBody() {
        @Override
        public void writeTo(OutputStream outputStream) throws IOException {
            PMDController.this.callURL(response, request, outputStream);
        }
    };
}

但现在响应更改为 XML 并且在从服务器端收集到完整响应之前不会打印 JS console.log('Data from server'); 行。

当响应更改为 XML 时,我如何将其解析为 JSON 格式,因为我的完整 JS 取决于 JSON?

流式传输后为什么 angular 在完整请求完成后显示结果?

我找到了一个读取 StreamingBodyResponse 的库,它们在内部将 blob 类型更改为 JSON,这正是我所需要的:

@RequestMapping("/utilities/longProcessStream")
public StreamingResponseBody asyncLongProcessStream(HttpServletResponse response, HttpServletRequest request) {
    response.addHeader("Content-Type", MediaType.APPLICATION_JSON);
    return new StreamingResponseBody() {
        @Override
        public void writeTo(OutputStream outputStream) throws IOException {
            PMDController.this.callURL(response, request, outputStream);
        }
    };
}

JS:

$('#loaderImage').show();

$scope.sampleJSONClass = {};
$scope.sampleJSONTrigger = {};
$scope.sampleJSONPages = {};
oboe('/utilities/longProcessStream')
    .done(function(data) {
        var dataFromServer = data.pmdStructureWrapper;
        if (Object.keys(dataFromServer)[0].endsWith('.cls')) {
            $scope.sampleJSONClass[Object.keys(dataFromServer)[0]] = Object.values(dataFromServer)[0];
        }
        if (Object.keys(dataFromServer)[0].endsWith('.trigger')) {
            $scope.sampleJSONTrigger[Object.keys(dataFromServer)[0]] = Object.values(dataFromServer)[0];
        }
        if (Object.keys(dataFromServer)[0].endsWith('.page')) {
            $scope.sampleJSONPages[Object.keys(dataFromServer)[0]] = Object.values(dataFromServer)[0];
        }
        $scope.$apply();
        $('#loaderImage').hide();
    })
    .fail(function() {
        console.log('error');
    });

这非常有效。