feat($browser.$defer.cancel): support canceling defered tasks

This commit is contained in:
Igor Minar 2011-05-28 01:48:14 -07:00
parent 120701b9d9
commit 2b2df4754d
3 changed files with 86 additions and 11 deletions

View file

@ -40,6 +40,8 @@ function Browser(window, document, body, XHR, $log) {
rawDocument = document[0], rawDocument = document[0],
location = window.location, location = window.location,
setTimeout = window.setTimeout, setTimeout = window.setTimeout,
clearTimeout = window.clearTimeout,
pendingDeferIds = {},
lastLocationUrl; lastLocationUrl;
self.isMock = false; self.isMock = false;
@ -163,15 +165,12 @@ function Browser(window, document, body, XHR, $log) {
* @returns {function()} the added function * @returns {function()} the added function
*/ */
self.addPollFn = function(fn) { self.addPollFn = function(fn) {
if (!pollTimeout) startPoller(100, setTimeout); if (isUndefined(pollTimeout)) startPoller(100, setTimeout);
pollFns.push(fn); pollFns.push(fn);
return fn; return fn;
}; };
/** /**
* @name angular.service.$browser#startPoller
* @methodOf angular.service.$browser
*
* @param {number} interval How often should browser call poll functions (ms) * @param {number} interval How often should browser call poll functions (ms)
* @param {function()} setTimeout Reference to a real or fake `setTimeout` function. * @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
* *
@ -339,20 +338,49 @@ function Browser(window, document, body, XHR, $log) {
* @methodOf angular.service.$browser * @methodOf angular.service.$browser
* @param {function()} fn A function, who's execution should be defered. * @param {function()} fn A function, who's execution should be defered.
* @param {number=} [delay=0] of milliseconds to defer the function execution. * @param {number=} [delay=0] of milliseconds to defer the function execution.
* @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
* *
* @description * @description
* Executes a fn asynchroniously via `setTimeout(fn, delay)`. * Executes a fn asynchroniously via `setTimeout(fn, delay)`.
* *
* Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
* `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed via * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
* `$browser.defer.flush()`. * via `$browser.defer.flush()`.
* *
*/ */
self.defer = function(fn, delay) { self.defer = function(fn, delay) {
var timeoutId;
outstandingRequestCount++; outstandingRequestCount++;
setTimeout(function() { completeOutstandingRequest(fn); }, delay || 0); timeoutId = setTimeout(function() {
delete pendingDeferIds[timeoutId];
completeOutstandingRequest(fn);
}, delay || 0);
pendingDeferIds[timeoutId] = true;
return timeoutId;
}; };
/**
* @workInProgress
* @ngdoc method
* @name angular.service.$browser.defer#cancel
* @methodOf angular.service.$browser.defer
* @returns {boolean} Returns `true` if the task hasn't executed yet and was successfuly canceled.
*
* @description
* Cancels a defered task identified with `deferId`.
*/
self.defer.cancel = function(deferId) {
if (pendingDeferIds[deferId]) {
delete pendingDeferIds[deferId];
clearTimeout(deferId);
completeOutstandingRequest(noop);
return true;
}
};
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
// Misc API // Misc API
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////

19
src/angular-mocks.js vendored
View file

@ -281,15 +281,32 @@ function MockBrowser() {
self.cookieHash = {}; self.cookieHash = {};
self.lastCookieHash = {}; self.lastCookieHash = {};
self.deferredFns = []; self.deferredFns = [];
self.deferredNextId = 0;
self.defer = function(fn, delay) { self.defer = function(fn, delay) {
delay = delay || 0; delay = delay || 0;
self.deferredFns.push({time:(self.defer.now + delay), fn:fn}); self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
self.deferredFns.sort(function(a,b){ return a.time - b.time;}); self.deferredFns.sort(function(a,b){ return a.time - b.time;});
return self.deferredNextId++;
}; };
self.defer.now = 0; self.defer.now = 0;
self.defer.cancel = function(deferId) {
var fnIndex;
forEach(self.deferredFns, function(fn, index) {
if (fn.id === deferId) fnIndex = index;
});
if (fnIndex) {
self.deferredFns.splice(fnIndex, 1);
}
}
self.defer.flush = function(delay) { self.defer.flush = function(delay) {
if (angular.isDefined(delay)) { if (angular.isDefined(delay)) {
self.defer.now += delay; self.defer.now += delay;

View file

@ -5,8 +5,11 @@ describe('browser', function(){
var browser, fakeWindow, xhr, logs, scripts, removedScripts, setTimeoutQueue; var browser, fakeWindow, xhr, logs, scripts, removedScripts, setTimeoutQueue;
function fakeSetTimeout(fn) { function fakeSetTimeout(fn) {
setTimeoutQueue.push(fn); return setTimeoutQueue.push(fn) - 1; //return position in the queue
return Math.random(); }
function fakeClearTimeout(deferId) {
setTimeoutQueue[deferId] = noop; //replace fn with noop to preserve other deferId indexes
} }
fakeSetTimeout.flush = function() { fakeSetTimeout.flush = function() {
@ -25,7 +28,8 @@ describe('browser', function(){
xhr = null; xhr = null;
fakeWindow = { fakeWindow = {
location: {href:"http://server"}, location: {href:"http://server"},
setTimeout: fakeSetTimeout setTimeout: fakeSetTimeout,
clearTimeout: fakeClearTimeout
}; };
var fakeBody = [{appendChild: function(node){scripts.push(node);}, var fakeBody = [{appendChild: function(node){scripts.push(node);},
@ -161,6 +165,32 @@ describe('browser', function(){
fakeSetTimeout.flush(); fakeSetTimeout.flush();
expect(callback).toHaveBeenCalled(); expect(callback).toHaveBeenCalled();
}); });
it('should return unique deferId', function() {
var deferId1 = browser.defer(noop),
deferId2 = browser.defer(noop);
expect(deferId1).toBeDefined();
expect(deferId2).toBeDefined();
expect(deferId1).not.toEqual(deferId2);
})
describe('cancel', function() {
it('should allow tasks to be canceled with returned deferId', function() {
var log = [],
deferId1 = browser.defer(function() { log.push('cancel me') }),
deferId2 = browser.defer(function() { log.push('ok') }),
deferId3 = browser.defer(function() { log.push('cancel me, now!') });
expect(log).toEqual([]);
browser.defer.cancel(deferId1);
browser.defer.cancel(deferId3);
fakeSetTimeout.flush();
expect(log).toEqual(['ok']);
});
});
}); });