AngularJS ile sunucu yoklaması


86

AngularJS öğrenmeye çalışıyorum. Her saniye yeni veri alma girişimim işe yaradı:

'use strict';

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

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

İş parçacığı 5 saniye boyunca uyuyarak yavaş bir sunucuyu simüle ettiğimde, kullanıcı arayüzünü güncellemeden ve başka bir zaman aşımı ayarlamadan önce yanıtı bekler. Sorun, modül oluşturmak için Açısal modülleri ve DI'yi kullanmak için yukarıdakileri yeniden yazdığımda:

'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);
    })();
};

Bu yalnızca sunucu yanıtı hızlıysa çalışır. Herhangi bir gecikme olursa, yanıt beklemeden saniyede 1 istek gönderir ve kullanıcı arayüzünü temizler gibi görünür. Bir geri arama işlevi kullanmam gerektiğini düşünüyorum. Denedim:

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

ancak bir hata aldım: "Hata: destination.push bir işlev değil" Bu, $ resource belgelerine dayanıyordu ancak oradaki örnekleri gerçekten anlamadım.

İkinci yaklaşımı nasıl çalıştırırım?

Yanıtlar:


115

İçin tickgeri aramada işlevi çağırmalısınız query.

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

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

3
Mükemmel, teşekkürler. Geri aramayı oraya koyabileceğinizi bilmiyordum. Bu, spam sorununu çözdü. Ayrıca, veri atamasını geri aramanın içine taşıdım ve bu da UI temizleme sorununu çözdü.
Dave

1
Yardım edebildiğime sevindim! Eğer bu sorunu çözdüyse, bu cevabı kabul edebilirsin, böylece daha sonra başkaları da yararlanabilir.
abhaga

1
Yukarıdaki kodun sayfaA ve denetleyiciA için olduğunu varsayarsak. B sayfasına ve B denetleyicisine gittiğimde bu zamanlayıcıyı nasıl durdururum?
Varun Verma

6
$ Zaman aşımını durdurma süreci burada docs.angularjs.org/api/ng.$timeout açıklanmaktadır . Temel olarak, $ timeout işlevi bir değişkene atamanız gereken bir söz verir. Sonra bu denetleyicinin ne zaman yok edileceğini dinleyin: $ kapsam. $ On ('destroy', fn ()) ;. Geri arama işlevinde, $ timeout'un iptal yöntemini çağırın ve kaydettiğiniz sözü iletin: $ timeout.cancel (timeoutVar); $ Aralığı belgelerinin aslında daha iyi bir örneği var ( docs.angularjs.org/api/ng.$interval )
Justin Lucas

1
@JustinLucas, her ihtimale karşı $ kapsam olmalıdır. $ On ('$ destroy', fn ());
Domates

33

Angular'ın daha yeni sürümleri, sunucu yoklaması için $ timeout'tan bile daha iyi çalışan $ interval getirmiştir .

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'in uygun olduğunu düşünmüyorum, çünkü sonraki isteği göndermeden önce sunucunun yanıtını bekleyemezsiniz. Bu, sunucunun yüksek bir gecikme süresine sahip olduğu durumlarda birçok istekte bulunulmasına neden olabilir.
Treur

4
@Treur: Bugünlerde bu geleneksel bir bilgelik gibi görünse de, kabul ettiğimden emin değilim. Çoğu durumda daha esnek bir çözüme sahip olmayı tercih ederim. Bir kullanıcının geçici olarak çevrimdışı olduğu durumu veya sunucunun tek bir isteğe yanıt vermediği durumunuzun en uç noktasını düşünün. Yeni bir zaman aşımı ayarlanmayacağından, kullanıcı arayüzü $ timeout kullanıcıları için güncellemeyi durduracak. $ İnterval kullanıcıları için, arayüz, bağlantı yeniden kurulur kurulmaz kaldığı yerden devam edecektir. Açıkçası aklı başında gecikmeleri seçmek de önemlidir.
Bob

2
Bence daha uygun ama dirençli değil. (Yatak odamdaki bir tuvalet geceleri de çok kullanışlıdır, ancak sonunda kötü kokmaya başlayacaktır;)) $ interval kullanarak gerçek verileri alırken sunucu sonuçlarını görmezden gelirsiniz. Bu, kullanıcınızı bilgilendirmek, veri bütünlüğünü kolaylaştırmak veya kısaca: genel olarak uygulama durumunuzu yönetmek için bir yöntemden yoksundur. Ancak, bunun için yaygın $ http engelleyicileri kullanabilir ve bu olduğunda $ aralığını iptal edebilirsiniz.
Treur

2
$ Q taahhütlerini kullanıyorsanız, istek başarısız olsa da olmasa da yoklamanın devam ettiğinden emin olmak için son geri aramayı kullanabilirsiniz.
Tyson Nero

8
Daha iyi bir alternatif, sadece başarı olayını değil, aynı zamanda hata olayını da ele almak olacaktır. Bu şekilde, başarısız olursa isteği tekrar deneyebilirsiniz. Hatta farklı bir aralıkta bile yapabilirsiniz ...
Peanut

5

İşte yinelemeli yoklama kullanan versiyonum. Bu, bir sonraki zaman aşımını başlatmadan önce sunucunun yanıtını bekleyeceği anlamına gelir. Ayrıca, bir hata oluştuğunda, sorgulamaya devam edecek, ancak daha rahat bir malikanede ve hatanın süresine göre.

Demo burada

Bunun hakkında burada daha fazla yazdım

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

$ İnterval hizmetini kullanarak kolayca yoklama yapabiliriz. $ interval
https://docs.angularjs.org/api/ng/service/$interval ile ilgili ayrıntılı belge burada $ interval kullanımıyla ilgili
problem , $ http hizmet çağrısı veya sunucu etkileşimi yapıyorsanız ve $ interval süresinden fazla geciktiyseniz daha sonra bir isteğiniz tamamlanmadan başka bir istek başlatır.
Çözüm:
1. Yoklama, tek bit veya hafif bir json gibi sunucudan alınan basit bir durum olmalıdır, bu nedenle tanımladığınız aralık süresinden daha uzun sürmemelidir. Bu sorunu önlemek için aralık süresini de uygun şekilde tanımlamalısınız.
2. Her nasılsa, herhangi bir nedenle hala devam ediyor, başka bir istek göndermeden önce önceki isteğin bitip bitmediğini genel bir bayrağı kontrol etmelisiniz. Bu zaman aralığını kaçıracak, ancak erken istek göndermeyecek.
Ayrıca, bir değerden sonra herhangi bir değerden sonra sorgulamanın ayarlanması gereken bir eşik değeri ayarlamak istiyorsanız, bunu aşağıdaki şekilde yapabilirsiniz.
İşte çalışma örneği. burada ayrıntılı olarak açıklanmıştır

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;
    }
}]);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.