A while ago, I had to write a polling service in AngularJS. For this service, there were three main requirements:
- poll the backend every x ms until the expected result has been attained
- the service has to timeout after a set period of time
- one should be able to stop the polling
The polling and the timeout were fairly trivial to implement. There was only one gotcha. If you’re using Protractor for e2e testing, you have to use $interval
instead of $timeout
. Protractor has issues figuring out when AngularJS is done with its work when you use $timeout
. With the $interval
service it behaves like expected.
The tricky part for me turned out to be the third requirement. How do you cancel the polling? Eventually, I figured out that it had to be done through cancelling the $interval
/$timeout
.
To summarise, here is an example that outlines what I’ve come up with:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
(function () { 'use strict'; angular .module('it.jdev.examples') .factory('PollingService', PollingService); PollingService.$inject = ['$q', '$http', '$interval']; function PollingService($q, $http, $interval) { var currentlyRunningRequest = null; var api = { getResults: getResults, cancel: cancel }; return api; function cancel() { if (currentlyRunningRequest) { $interval.cancel(currentlyRunningRequest); } currentlyRunningRequest = null; } function getResults() { var deferred = $q.defer(); cancel(); // Cancel any previous request; var getData = function () { $http.get('http://examples.jdev.it/my_example_service').then( function (data) { if (data.myAttribute === 'Value I\'m waiting for') { deferred.resolve(data); } else if (hasTimedOut()) { deferred.reject('Timed out'); } else { currentlyRunningRequest = $interval(getData, 1000, 1); // retry in one second } }, function (error) { deferred.reject(error); } ); }; getData(); var startTime = new Date().getTime(); var hasTimedOut = function () { var endTime = new Date().getTime(); return (endTime - startTime) > (5 * 60 * 1000); // timeout after 5 minutes }; return deferred.promise; } } })(); |