Máy chủ thăm dò với AngularJS


86

Tôi đang cố gắng học AngularJS. Nỗ lực đầu tiên của tôi để lấy dữ liệu mới mỗi giây đã thành công:

'use strict';

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

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

Khi tôi mô phỏng một máy chủ chậm bằng cách ngủ chuỗi trong 5 giây, nó sẽ đợi phản hồi trước khi cập nhật giao diện người dùng và đặt thời gian chờ khác. Vấn đề là khi tôi viết lại phần trên để sử dụng mô-đun Angular và DI để tạo mô-đun:

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

Điều này chỉ hoạt động nếu phản hồi của máy chủ nhanh. Nếu có bất kỳ sự chậm trễ nào, nó sẽ gửi ra 1 yêu cầu mỗi giây mà không cần đợi phản hồi và dường như xóa giao diện người dùng. Tôi nghĩ rằng tôi cần sử dụng một hàm gọi lại. Tôi đã thử:

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

nhưng gặp lỗi: "Error: destination.push không phải là một hàm" Điều này dựa trên tài liệu cho $ resource nhưng tôi không thực sự hiểu các ví dụ ở đó.

Làm cách nào để làm cho cách tiếp cận thứ hai hoạt động?

Câu trả lời:


115

Bạn nên gọi tickhàm trong lệnh gọi lại cho query.

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

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

3
Cảm ơn vô cùng. Tôi không biết bạn có thể gọi lại ở đó. Điều đó đã giải quyết được vấn đề gửi thư rác. Tôi cũng đã chuyển việc gán dữ liệu vào bên trong lệnh gọi lại để giải quyết vấn đề xóa giao diện người dùng.
Dave

1
Rât vui long khi giup đơ! Nếu điều này giải quyết được vấn đề, bạn có thể chấp nhận câu trả lời này để những người khác sau này cũng có thể hưởng lợi từ nó.
abhaga

1
Giả sử đoạn mã trên dành cho pageA và controllerA. Làm cách nào để dừng bộ đếm thời gian này khi tôi điều hướng đến pageB và controllerB?
Varun Verma

6
Quy trình dừng $ timeout được giải thích tại đây docs.angularjs.org/api/ng.$timeout . Về cơ bản, hàm $ timeout trả về một lời hứa mà bạn cần gán cho một biến. Sau đó, lắng nghe khi bộ điều khiển đó bị phá hủy: $ scope. $ On ('Destop', fn ()) ;. Trong hàm gọi lại, hãy gọi phương thức hủy của $ timeout và chuyển vào lời hứa mà bạn đã lưu: $ timeout.cancel (timeoutVar); Tài liệu $ khoảng thời gian thực sự có một ví dụ tốt hơn ( docs.angularjs.org/api/ng.$interval )
Justin Lucas

1
@JustinLucas, đề phòng nó phải là $ scope. $ On ('$ Destop', fn ());
Tomato,

33

Các phiên bản gần đây hơn của angle đã giới thiệu $ khoảng thời gian hoạt động thậm chí còn tốt hơn $ timeout để thăm dò máy chủ.

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, Tôi không nghĩ rằng $ khoảng thời gian là phù hợp, vì bạn không thể đợi phản hồi của máy chủ trước khi gửi yêu cầu tiếp theo. Điều này có thể dẫn đến nhiều yêu cầu khi máy chủ có độ trễ cao.
Treur

4
@Treur: Mặc dù điều đó có vẻ là thông thường ngày nay, nhưng tôi không chắc mình đồng ý. Trong hầu hết các trường hợp, tôi muốn có một giải pháp linh hoạt hơn. Hãy xem xét trường hợp người dùng tạm thời ngoại tuyến hoặc trường hợp cực đoan của bạn khi máy chủ không phản hồi một yêu cầu duy nhất. Giao diện người dùng sẽ ngừng cập nhật cho người dùng $ timeout vì thời gian chờ mới sẽ không được đặt. Đối với người dùng $ khoảng, giao diện người dùng sẽ bắt đầu từ nơi nó dừng lại ngay khi kết nối được khôi phục. Rõ ràng việc chọn lựa sự chậm trễ lành mạnh cũng rất quan trọng.
Bob

2
Tôi nghĩ rằng nó là thuận tiện hơn, nhưng không kiên cường. (Một nhà vệ sinh trong phòng ngủ của tôi cũng rất thuận tiện vào ban đêm, nhưng cuối cùng nó sẽ bắt đầu có mùi hôi;)) Khi truy xuất dữ liệu thực tế bằng cách sử dụng $ khoảng, bạn bỏ qua kết quả máy chủ. Điều này thiếu một phương pháp để thông báo cho người dùng của bạn, tạo điều kiện toàn vẹn dữ liệu hay nói ngắn gọn là: quản lý trạng thái ứng dụng của bạn nói chung. Tuy nhiên, bạn có thể sử dụng các trình chặn $ http phổ biến cho việc này và hủy khoảng thời gian $ khi điều này xảy ra.
Treur

2
Nếu sử dụng các hứa hẹn $ q, bạn có thể chỉ cần sử dụng lệnh gọi lại cuối cùng để đảm bảo việc thăm dò vẫn tiếp tục cho dù yêu cầu không thành công hay không.
Tyson Nero

8
Một giải pháp thay thế tốt hơn sẽ là xử lý không chỉ sự kiện thành công mà còn cả sự kiện lỗi. Bằng cách này, bạn có thể thử lại yêu cầu nếu nó không thành công. Bạn thậm chí có thể làm điều đó tại một khoảng thời gian khác nhau ...
Đậu phộng

5

Đây là phiên bản của tôi sử dụng thăm dò đệ quy. Có nghĩa là nó sẽ đợi phản hồi của máy chủ trước khi bắt đầu thời gian chờ tiếp theo. Ngoài ra, khi xảy ra lỗi, nó sẽ tiếp tục bỏ phiếu nhưng trong một trang viên thoải mái hơn và theo thời gian xảy ra lỗi.

Demo ở đây

Đã viết thêm về nó tại đây

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

Chúng tôi có thể thực hiện việc thăm dò ý kiến ​​một cách dễ dàng bằng dịch vụ $ khoảng thời gian. đây là tài liệu chi tiết về $ khoảng thời gian
https://docs.angularjs.org/api/ng/service/$interval
Vấn đề khi sử dụng $ khoảng thời gian là nếu bạn đang gọi dịch vụ $ http hoặc tương tác máy chủ và nếu bị trì hoãn hơn $ khoảng thời gian thì trước khi một yêu cầu của bạn hoàn thành, nó sẽ bắt đầu một yêu cầu khác.
Giải pháp:
1. Thăm dò ý kiến ​​phải là trạng thái đơn giản nhận được từ máy chủ như một bit đơn hoặc json nhẹ, do đó sẽ không mất nhiều thời gian hơn khoảng thời gian xác định của bạn. Bạn cũng nên xác định khoảng thời gian thích hợp để tránh vấn đề này.
2. Bằng cách nào đó nó vẫn xảy ra do bất kỳ lý do gì, bạn nên kiểm tra một cờ toàn cầu rằng yêu cầu trước đó đã hoàn thành hay chưa trước khi gửi bất kỳ yêu cầu nào khác. Nó sẽ bỏ lỡ khoảng thời gian đó nhưng nó sẽ không gửi yêu cầu sớm.
Ngoài ra, nếu bạn muốn đặt giá trị ngưỡng mà sau đó giá trị nào đó sẽ được đặt, thì bạn có thể thực hiện theo cách sau.
Đây là ví dụ làm việc. giải thích chi tiết tại đây

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;
    }
}]);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.