2013-02-18 47 views
30

我正在使用AngularJS構建一個web應用程序。該應用需要輪詢一個返回JSON數據的URL,並將該數據提供給該應用的任何部分。從我目前閱讀的內容來看,我最好的選擇是創建一個服務來處理輪詢並保留其自己的JSON數據的內部緩存,然後將該服務注入想要查閱該數據的應用程序的任何部分。我失去的是如何真正去做這件事。我發現的最接近的例子是this question,但它似乎是創建一個由特定控制器(它本身綁定到給定路由)手動調用的服務,而我想要永久運行在應用程序背景中的東西永遠不管應用的哪一部分是活動的。這是可行的,還是我採取了完全錯誤的方法?AngularJS全球http輪詢服務

回答

41

這裏我的解決辦法:

app.factory('Poller', function($http, $timeout) { 
    var data = { response: {}, calls: 0 }; 
    var poller = function() { 
    $http.get('data.json').then(function(r) { 
     data.response = r.data; 
     data.calls++; 
     $timeout(poller, 1000); 
    });  
    }; 
    poller(); 

    return { 
    data: data 
    }; 
}); 

(電話只是爲了顯示輪詢已完成)

http://plnkr.co/edit/iMmhXTYweN4IrRrrpvMq?p=preview

編輯:隨着約什大衛·米勒在意見提出,依賴於這個應該在app.run塊中添加服務以確保從開始執行輪詢:

app.run(function(Poller) {}); 

並且還在上次調用完成後移動了下一輪的調度。因此,如果投票長時間掛起,那麼不會有「堆疊」電話。

更新的重擊器。

+2

+1但是他想要的東西總是運行,因此也從'app.run' BLO觸發它確保它從一開始就運行。我還會檢查'poller'函數以確保先前調用'$ http'完成,所以它們不會無限累積,但似乎沒有辦法通過AngularJS的承諾實現。 – 2013-02-18 23:53:41

+1

Thnx。兩個項目都加入了答案。關於堆疊可以通過簡單地將下一個輪詢的時間安排移動到先前呼叫的「隨後」來避免。不是嗎? – 2013-02-19 09:28:01

+1

太棒了! :-)我想我非常關注'isFulfilled()'的原始'q'的承諾方法,即'$ q'沒有實現,我錯過了完全明顯的。大聲笑 – 2013-02-19 18:45:34

20

以下是Github上的angular poller service,它可以很容易地注入到控制器中。

要安裝: bower install angular-poller

既然你想開始在後臺一直運行一個全球性的查詢服務,你可以這樣做:

// Inject angular poller service. 
var myModule = angular.module('myApp', ['poller']); 

// The home/init controller when you start the app. 
myModule.controller('myController', function($scope, $resource, poller) { 

    // Define your resource object. 
    var myResource = $resource(url[, paramDefaults]); 

    // Create and start poller. 
    var myPoller = poller.get(myResource); 

    // Update view. Most likely you only need to define notifyCallback. 
    myPoller.promise.then(successCallback, errorCallback, notifyCallback); 
}); 

現在,它會在後臺,直到調用myPoller.stop()poller.stopAll()運行永遠。

如果您想使用此輪詢其他控制器的回調數據,你可以簡單地做:

myModule.controller('anotherController', function($scope, $resource, poller) { 

    /* 
    * You can also move this to a $resource factory and inject it 
    * into the controller so you do not have to define it twice. 
    */ 
    var sameResource = $resource(url[, paramDefaults]); 

    /* 
    * This will not create a new poller for the same resource 
    * since it already exists, but will simply restarts it. 
    */ 
    var samePoller = poller.get(sameResource); 

    samePoller.promise.then(successCallback, errorCallback, notifyCallback); 
}); 
+0

它看起來像一個偉大的圖書館。有什麼樣的應用程序嗎? – ardochhigh 2014-04-02 21:35:12

+0

@ardochhigh我打算很快添加一個示例應用程序。但在此之前,您可以檢出[readme](https://github.com/emmaguo/angular-poller/blob/master/README.md)頁面。 :-) – 2014-04-02 22:08:12

+0

謝謝@Emma Guo README頁面非常詳細。我對Angular相當陌生,但仍然對一些概念感到困惑。您的圖書館看起來非常適合我的要求也許我會試着做一個重要的概念證明。我會告訴你。 – ardochhigh 2014-04-03 08:06:53

0

我分叉@ ValentynShybanov的工廠代碼並添加間隔調用(每秒,每5秒,等),你也可以停止和啓動輪詢如你所願:

http://plnkr.co/edit/EfsttAc4BtWSUiAU2lWf?p=preview

app.factory('Poller', function($http, $timeout) { 
    var pollerData = { 
    response: {}, 
    calls: 0, 
    stop: false 
    }; 

    var isChannelLive = function() { 
    $http.get('data.json').then(function(r) { 
     if (pollerData.calls > 30 && pollerData.stop === false) { // call every minute after the first ~30 secs 
     var d = new Date(); 
     console.log('> 30: ' + d.toLocaleString() + ' - count: ' + pollerData.calls); 
     pollerData.calls++; 
     $timeout(isChannelLive, 10000); 
     } else if (pollerData.calls > 15 && pollerData.calls <= 30 && pollerData.stop === false) { // after the first ~15 secs, then call every 5 secs 
     var d = new Date(); 
     console.log('> 15 & <= 30: ' + d.toLocaleString() + ' - count: ' + pollerData.calls); 
     pollerData.calls++; 
     $timeout(isChannelLive, 5000); 
     } else if (pollerData.calls <= 15 && pollerData.stop === false) { // call every 1 second during the first ~15 seconds 
     var d = new Date(); 
     console.log('<= 15: ' + d.toLocaleString() + ' - count: ' + pollerData.calls); 
     pollerData.calls++; 
     $timeout(isChannelLive, 1000); 
     } 

     pollerData.response = r.data; 
    }); 

    }; 
    var init = function() { 
    if (pollerData.calls === 0) { 
     pollerData.stop = false; 
     isChannelLive(); 
    } 
    }; 
    var stop = function() { 
    pollerData.calls = 0; 
    pollerData.stop = true; 
    }; 

    return { 
    pollerData: pollerData, // this should be private 
    init: init, 
    stop: stop 
    }; 
});