AngularJS를 사용한 서버 폴링


86

AngularJS를 배우려고합니다. 매초마다 새 데이터를 얻으려는 첫 번째 시도가 효과가있었습니다.

'use strict';

function dataCtrl($scope, $http, $timeout) {
    $scope.data = [];

    (function tick() {
        $http.get('api/changingData').success(function (data) {
            $scope.data = data;
            $timeout(tick, 1000);
        });
    })();
};

5 초 동안 스레드를 휴면하여 느린 서버를 시뮬레이션하면 UI를 업데이트하고 다른 시간 제한을 설정하기 전에 응답을 기다립니다. 문제는 모듈 생성을 위해 Angular 모듈과 DI를 사용하도록 위의 내용을 다시 작성할 때입니다.

'use strict';

angular.module('datacat', ['dataServices']);

angular.module('dataServices', ['ngResource']).
    factory('Data', function ($resource) {
        return $resource('api/changingData', {}, {
            query: { method: 'GET', params: {}, isArray: true }
        });
    });

function dataCtrl($scope, $timeout, Data) {
    $scope.data = [];

    (function tick() {
        $scope.data = Data.query();
        $timeout(tick, 1000);
    })();
};

이것은 서버 응답이 빠른 경우에만 작동합니다. 지연이 발생하면 응답을 기다리지 않고 1 초에 1 번의 요청을 스팸 처리하고 UI를 지우는 것처럼 보입니다. 콜백 함수를 사용해야한다고 생각합니다. 나는 시도했다 :

var x = Data.get({}, function () { });

하지만 오류가 발생했습니다. "오류 : destination.push는 함수가 아닙니다." $ resource 했지만 실제로 예제를 이해하지 못했습니다.

두 번째 접근 방식은 어떻게 작동합니까?

답변:


115

tick에 대한 콜백 에서 함수를 호출해야합니다 query.

function dataCtrl($scope, $timeout, Data) {
    $scope.data = [];

    (function tick() {
        $scope.data = Data.query(function(){
            $timeout(tick, 1000);
        });
    })();
};

3
훌륭 해요, 고마워요. 거기에 콜백을 넣을 수 있을지 몰랐습니다. 스팸 문제가 해결되었습니다. 또한 UI 지우기 문제를 해결 한 콜백 내부로 데이터 할당을 옮겼습니다.
Dave

1
도와 드릴 수있어서 기쁩니다! 이 방법으로 문제가 해결 된 경우 나중에 다른 사람에게도 도움이되도록이 답변을 수락 할 수 있습니다.
abhaga

1
위의 코드가 pageA 및 controllerA 용이라고 가정합니다. pageB 및 controllerB로 이동할 때이 타이머를 어떻게 중지합니까?
Varun Verma 2013

6
$ timeout 중지 프로세스는 여기 docs.angularjs.org/api/ng.$timeout에 설명되어 있습니다 . 기본적으로 $ timeout 함수는 변수에 할당해야하는 promise를 반환합니다. 그런 다음 해당 컨트롤러가 파괴 될 때 수신합니다 : $ scope. $ on ( 'destroy', fn ()) ;. 콜백 함수에서 $ timeout의 cancel 메소드를 호출하고 저장 한 promise를 전달합니다. $ timeout.cancel (timeoutVar); $ interval 문서에는 실제로 더 나은 예가 있습니다 ( docs.angularjs.org/api/ng.$interval )
Justin Lucas

1
@JustinLucas, $ scope. $ on ( '$ destroy', fn ());
Tomato

33

최신 버전의 angular는 $ interval 을 도입 하여 서버 폴링에 대해 $ timeout 보다 훨씬 더 잘 작동 합니다.

var refreshData = function() {
    // Assign to scope within callback to avoid data flickering on screen
    Data.query({ someField: $scope.fieldValue }, function(dataElements){
        $scope.data = dataElements;
    });
};

var promise = $interval(refreshData, 1000);

// Cancel interval on page changes
$scope.$on('$destroy', function(){
    if (angular.isDefined(promise)) {
        $interval.cancel(promise);
        promise = undefined;
    }
});

17
-1, 다음 요청을 보내기 전에 서버 응답을 기다릴 수 없기 때문에 $ interval이 적합하지 않다고 생각합니다. 이로 인해 서버의 대기 시간이 길면 많은 요청이 발생할 수 있습니다.
Treur 2014 년

4
@Treur : 요즘은 그게 일반적인 통념 인 것 같지만 동의하는지 모르겠습니다. 대부분의 경우 더 탄력적 인 솔루션을 원합니다. 사용자가 일시적으로 오프라인 상태가되는 경우 또는 서버가 단일 요청에 응답하지 않는 극단적 인 경우를 고려하십시오. 새 시간 제한이 설정되지 않으므로 $ timeout 사용자에 대한 UI 업데이트가 중지됩니다. $ interval 사용자의 경우 연결이 복원되는 즉시 중단 된 지점에서 UI가 표시됩니다. 분명히 정상적인 지연을 선택하는 것도 중요합니다.

2
나는 그것이 더 편리하다고 생각하지만 탄력적이지는 않습니다. (제 침실의 화장실도 밤에 매우 편리하지만 결국 냄새가 나기 시작합니다.)) $ interval을 사용하여 실제 데이터를 검색 할 때 서버 결과를 무시합니다. 여기에는 사용자에게 알리거나 데이터 무결성을 촉진하거나 간단히 말하자면 일반적으로 애플리케이션 상태를 관리하는 방법이 없습니다. 그러나이를 위해 일반적인 $ http 인터셉터를 사용하고이 경우 $ interval을 취소 할 수 있습니다.
Treur 2014 년

2
$ q promise를 사용하는 경우 finally 콜백을 사용하여 요청이 실패했는지 여부에 관계없이 폴링이 계속되도록 할 수 있습니다.
Tyson Nero

8
더 나은 대안은 성공 이벤트뿐만 아니라 오류 이벤트도 처리하는 것입니다. 이렇게하면 요청이 실패 할 경우 다시 시도 할 수 있습니다. 다른 간격으로 할 수도 있습니다 ...
Peanut

5

다음은 재귀 폴링을 사용하는 내 버전입니다. 즉, 다음 시간 초과를 시작하기 전에 서버 응답을 기다립니다. 또한 오류가 발생하면 폴링을 계속하지만 더 편안한 방식으로 오류 기간에 따라 진행됩니다.

데모는 여기

여기에 그것에 대해 더 많이 적었습니다.

var app = angular.module('plunker', ['ngAnimate']);

app.controller('MainCtrl', function($scope, $http, $timeout) {

    var loadTime = 1000, //Load the data every second
        errorCount = 0, //Counter for the server errors
        loadPromise; //Pointer to the promise created by the Angular $timout service

    var getData = function() {
        $http.get('http://httpbin.org/delay/1?now=' + Date.now())

        .then(function(res) {
             $scope.data = res.data.args;

              errorCount = 0;
              nextLoad();
        })

        .catch(function(res) {
             $scope.data = 'Server error';
             nextLoad(++errorCount * 2 * loadTime);
        });
    };

     var cancelNextLoad = function() {
         $timeout.cancel(loadPromise);
     };

    var nextLoad = function(mill) {
        mill = mill || loadTime;

        //Always make sure the last timeout is cleared before starting a new one
        cancelNextLoad();
        $timeout(getData, mill);
    };


    //Start polling the data from the server
    getData();


        //Always clear the timeout when the view is destroyed, otherwise it will   keep polling
        $scope.$on('$destroy', function() {
            cancelNextLoad();
        });

        $scope.data = 'Loading...';
   });

0

$ interval 서비스를 사용하여 쉽게 폴링 할 수 있습니다. $ interval에 대한 자세한 문서입니다.
https://docs.angularjs.org/api/ng/service/$interval $ interval 사용
문제 는 $ http 서비스 호출 또는 서버 상호 작용을 수행하고 $ interval 시간 이상 지연되는 경우입니다. 그런 다음 한 요청이 완료되기 전에 다른 요청을 시작합니다.
솔루션 :
1. 폴링은 단일 비트 또는 경량 json과 같이 서버에서 얻는 간단한 상태 여야하므로 정의 된 간격 시간보다 오래 걸리지 않아야합니다. 이 문제를 방지하려면 간격 시간도 적절하게 정의해야합니다.
2. 여하튼 어떤 이유로 든 여전히 발생하고 있습니다. 다른 요청을 보내기 전에 이전 요청이 완료되었는지 여부를 글로벌 플래그를 확인해야합니다. 해당 시간 간격을 놓치지 만 너무 일찍 요청을 보내지는 않습니다.
또한 어떤 값이든 폴링을 설정 해야하는 임계 값을 설정하려면 다음과 같이 할 수 있습니다.
다음은 작동 예입니다. 여기 에 자세히 설명

angular.module('myApp.view2', ['ngRoute'])
.controller('View2Ctrl', ['$scope', '$timeout', '$interval', '$http', function ($scope, $timeout, $interval, $http) {
    $scope.title = "Test Title";

    $scope.data = [];

    var hasvaluereturnd = true; // Flag to check 
    var thresholdvalue = 20; // interval threshold value

    function poll(interval, callback) {
        return $interval(function () {
            if (hasvaluereturnd) {  //check flag before start new call
                callback(hasvaluereturnd);
            }
            thresholdvalue = thresholdvalue - 1;  //Decrease threshold value 
            if (thresholdvalue == 0) {
                $scope.stopPoll(); // Stop $interval if it reaches to threshold
            }
        }, interval)
    }

    var pollpromise = poll(1000, function () {
        hasvaluereturnd = false;
        //$timeout(function () {  // You can test scenario where server takes more time then interval
        $http.get('http://httpbin.org/get?timeoutKey=timeoutValue').then(
            function (data) {
                hasvaluereturnd = true;  // set Flag to true to start new call
                $scope.data = data;

            },
            function (e) {
                hasvaluereturnd = true; // set Flag to true to start new call
                //You can set false also as per your requirement in case of error
            }
        );
        //}, 2000); 
    });

    // stop interval.
    $scope.stopPoll = function () {
        $interval.cancel(pollpromise);
        thresholdvalue = 0;     //reset all flags. 
        hasvaluereturnd = true;
    }
}]);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.