Ostatnio, w jednym z projektów, musiałem wykonywać pewne zapytanie http co X sekund. Normalnie pewnie wszystko bym opakował w plugin jQuery (albo wykorzystał istniejący) i wykonywał polecenie ajax co jakiś czas.
Niestety, albo i stety, projekt oparłem na Angular a API dostępowe zrobiłem na bazie promise. Ze względu na to, że polling miał być wykonywany tylko w kilku miejscach, nie było sensu wsadzać tego do API, jak i pisać za każdym razem ręcznie polling dla konkretnej metody/strony.
Dlatego też stworzyłem coś takiego:
var app = angular.module('poller-wrapper', []); app.factory('poller', [ '$timeout', '$q', function ($timeout, $q) { var tasks = []; function Task(opts) { this.opts = opts; } Task.prototype = { start: function () { var deferred = $q.defer(), self = this; (function tick() { var p = self.opts.action.call(null, self.opts.params), rejected = false; p.then(function (data) { deferred.notify(data.data); }, function () { rejected = true; deferred.reject('Error occured'); }); self.timeoutId = $timeout(tick, self.opts.intevral); })(); this.promise = deferred.promise; return this; }, stop: function () { $timeout.cancel(this.timeoutId); this.timeoutId = null; } }; function create(opts) { var task = new Task(opts); tasks.push(task); return task; } function stopAll() { var i = 0; len = tasks.length; for (; i < len; i++) { tasks[i].stop(); } } function clear() { stopAll(); tasks = []; } return { create: create, stopAll: stopAll, clear: clear }; } ]);
Wykorzystanie jest dość proste, zakładamy tylko, że metoda którą będziemy wykonywać cyklicznie zwraca zmodyfikowany promise przez usługę $http:
var app = angular.module('some-app', ['poller-wrapper']); app.provider('api', function () { var baseUri = 'http://localhost'; this.baseUri = function (uri) { baseUri = uri || baseUri; }; function buildUri(uri) { return baseUri + uri; } this.$get = [ '$http', function ($http) { return { action: function () { return $http.get(buildUri('/action')); } }; } ]; }); app.controller('NameCtrl', [ '$http', 'poller', 'api', function ($http, poller, api) { var poll = poller.create({ intevral: 1000, action: api.action }); poll.start(); poll.promise.then(/*resolve*/ null,/*reject*/ null, /*notify*/ function () { $scope.data = data; }); poll.promise.catch((/*reject*/ function () { alert('error'); }); $scope.stop = function () { poll.stop(); }; } ]);
Co o tym sądzicie? Ma to ręce i nogi? Może da się to lepiej napisać? U mnie to śmiga, może powinienem jeszcze gdzieś testy tego wrzucić, ale to chyba będzie już inny post ;)