Skip to content

Commit 4f7b4b6

Browse files
committed
refactor($interval): share code between $interval and ngMock/$interval
This avoids code/logic duplication and helps the implementations stay in-sync.
1 parent 66d4a94 commit 4f7b4b6

File tree

5 files changed

+99
-117
lines changed

5 files changed

+99
-117
lines changed

angularFiles.js

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var angularFiles = {
2828
'src/ng/httpBackend.js',
2929
'src/ng/interpolate.js',
3030
'src/ng/interval.js',
31+
'src/ng/intervalFactory.js',
3132
'src/ng/jsonpCallbacks.js',
3233
'src/ng/locale.js',
3334
'src/ng/location.js',

src/AngularPublic.js

+2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
$FilterProvider,
7171
$$ForceReflowProvider,
7272
$InterpolateProvider,
73+
$$IntervalFactoryProvider,
7374
$IntervalProvider,
7475
$HttpProvider,
7576
$HttpParamSerializerProvider,
@@ -241,6 +242,7 @@ function publishExternalAPI(angular) {
241242
$$forceReflow: $$ForceReflowProvider,
242243
$interpolate: $InterpolateProvider,
243244
$interval: $IntervalProvider,
245+
$$intervalFactory: $$IntervalFactoryProvider,
244246
$http: $HttpProvider,
245247
$httpParamSerializer: $HttpParamSerializerProvider,
246248
$httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,

src/ng/interval.js

+13-48
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,18 @@ var $intervalMinErr = minErr('$interval');
44

55
/** @this */
66
function $IntervalProvider() {
7-
this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
8-
function($rootScope, $window, $q, $$q, $browser) {
7+
this.$get = ['$$intervalFactory', '$window',
8+
function($$intervalFactory, $window) {
99
var intervals = {};
10-
10+
var setIntervalFn = function(tick, delay, deferred) {
11+
var id = $window.setInterval(tick, delay);
12+
intervals[id] = deferred;
13+
return id;
14+
};
15+
var clearIntervalFn = function(id) {
16+
$window.clearInterval(id);
17+
delete intervals[id];
18+
};
1119

1220
/**
1321
* @ngdoc service
@@ -135,49 +143,7 @@ function $IntervalProvider() {
135143
* </file>
136144
* </example>
137145
*/
138-
function interval(fn, delay, count, invokeApply) {
139-
var hasParams = arguments.length > 4,
140-
args = hasParams ? sliceArgs(arguments, 4) : [],
141-
setInterval = $window.setInterval,
142-
clearInterval = $window.clearInterval,
143-
iteration = 0,
144-
skipApply = (isDefined(invokeApply) && !invokeApply),
145-
deferred = (skipApply ? $$q : $q).defer(),
146-
promise = deferred.promise;
147-
148-
count = isDefined(count) ? count : 0;
149-
150-
promise.$$intervalId = setInterval(function tick() {
151-
if (skipApply) {
152-
$browser.defer(callback);
153-
} else {
154-
$rootScope.$evalAsync(callback);
155-
}
156-
deferred.notify(iteration++);
157-
158-
if (count > 0 && iteration >= count) {
159-
deferred.resolve(iteration);
160-
clearInterval(promise.$$intervalId);
161-
delete intervals[promise.$$intervalId];
162-
}
163-
164-
if (!skipApply) $rootScope.$apply();
165-
166-
}, delay);
167-
168-
intervals[promise.$$intervalId] = deferred;
169-
170-
return promise;
171-
172-
function callback() {
173-
if (!hasParams) {
174-
fn(iteration);
175-
} else {
176-
fn.apply(null, args);
177-
}
178-
}
179-
}
180-
146+
var interval = $$intervalFactory(setIntervalFn, clearIntervalFn);
181147

182148
/**
183149
* @ngdoc method
@@ -205,8 +171,7 @@ function $IntervalProvider() {
205171
// Interval cancels should not report an unhandled promise.
206172
markQExceptionHandled(deferred.promise);
207173
deferred.reject('canceled');
208-
$window.clearInterval(id);
209-
delete intervals[id];
174+
clearIntervalFn(id);
210175

211176
return true;
212177
};

src/ng/intervalFactory.js

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
3+
/** @this */
4+
function $$IntervalFactoryProvider() {
5+
this.$get = ['$browser', '$q', '$$q', '$rootScope',
6+
function($browser, $q, $$q, $rootScope) {
7+
return function intervalFactory(setIntervalFn, clearIntervalFn) {
8+
return function intervalFn(fn, delay, count, invokeApply) {
9+
var hasParams = arguments.length > 4,
10+
args = hasParams ? sliceArgs(arguments, 4) : [],
11+
iteration = 0,
12+
skipApply = isDefined(invokeApply) && !invokeApply,
13+
deferred = (skipApply ? $$q : $q).defer(),
14+
promise = deferred.promise;
15+
16+
count = isDefined(count) ? count : 0;
17+
18+
function callback() {
19+
if (!hasParams) {
20+
fn(iteration);
21+
} else {
22+
fn.apply(null, args);
23+
}
24+
}
25+
26+
function tick() {
27+
if (skipApply) {
28+
$browser.defer(callback);
29+
} else {
30+
$rootScope.$evalAsync(callback);
31+
}
32+
deferred.notify(iteration++);
33+
34+
if (count > 0 && iteration >= count) {
35+
deferred.resolve(iteration);
36+
clearIntervalFn(promise.$$intervalId);
37+
}
38+
39+
if (!skipApply) $rootScope.$apply();
40+
}
41+
42+
promise.$$intervalId = setIntervalFn(tick, delay, deferred, skipApply);
43+
44+
return promise;
45+
};
46+
};
47+
}];
48+
}

src/ngMock/angular-mocks.js

+35-69
Original file line numberDiff line numberDiff line change
@@ -537,72 +537,40 @@ angular.mock.$LogProvider = function() {
537537
* @returns {promise} A promise which will be notified on each iteration.
538538
*/
539539
angular.mock.$IntervalProvider = function() {
540-
this.$get = ['$browser', '$rootScope', '$q', '$$q',
541-
function($browser, $rootScope, $q, $$q) {
540+
this.$get = ['$browser', '$$intervalFactory',
541+
function($browser, $$intervalFactory) {
542542
var repeatFns = [],
543543
nextRepeatId = 0,
544-
now = 0;
545-
546-
var $interval = function(fn, delay, count, invokeApply) {
547-
var hasParams = arguments.length > 4,
548-
args = hasParams ? Array.prototype.slice.call(arguments, 4) : [],
549-
iteration = 0,
550-
skipApply = (angular.isDefined(invokeApply) && !invokeApply),
551-
deferred = (skipApply ? $$q : $q).defer(),
552-
promise = deferred.promise;
553-
554-
count = (angular.isDefined(count)) ? count : 0;
555-
556-
promise.$$intervalId = nextRepeatId;
557-
558-
function tick() {
559-
if (skipApply) {
560-
$browser.defer(callback);
561-
} else {
562-
$rootScope.$evalAsync(callback);
563-
}
564-
deferred.notify(iteration++);
565-
566-
if (count > 0 && iteration >= count) {
567-
var fnIndex;
568-
deferred.resolve(iteration);
569-
570-
angular.forEach(repeatFns, function(fn, index) {
571-
if (fn.id === promise.$$intervalId) fnIndex = index;
544+
now = 0,
545+
setIntervalFn = function(tick, delay, deferred, skipApply) {
546+
var id = nextRepeatId++;
547+
var fn = !skipApply ? tick : function() {
548+
tick();
549+
$browser.defer.flush();
550+
};
551+
552+
repeatFns.push({
553+
nextTime: (now + (delay || 0)),
554+
delay: delay || 1,
555+
fn: fn,
556+
id: id,
557+
deferred: deferred
572558
});
559+
repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime; });
573560

574-
if (angular.isDefined(fnIndex)) {
575-
repeatFns.splice(fnIndex, 1);
561+
return id;
562+
},
563+
clearIntervalFn = function(id) {
564+
for (var fnIndex = repeatFns.length - 1; fnIndex >= 0; fnIndex--) {
565+
if (repeatFns[fnIndex].id === id) {
566+
repeatFns.splice(fnIndex, 1);
567+
break;
568+
}
576569
}
577-
}
578-
579-
if (skipApply) {
580-
$browser.defer.flush();
581-
} else {
582-
$rootScope.$apply();
583-
}
584-
}
570+
};
585571

586-
function callback() {
587-
if (!hasParams) {
588-
fn(iteration);
589-
} else {
590-
fn.apply(null, args);
591-
}
592-
}
572+
var $interval = $$intervalFactory(setIntervalFn, clearIntervalFn);
593573

594-
repeatFns.push({
595-
nextTime: (now + (delay || 0)),
596-
delay: delay || 1,
597-
fn: tick,
598-
id: nextRepeatId,
599-
deferred: deferred
600-
});
601-
repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
602-
603-
nextRepeatId++;
604-
return promise;
605-
};
606574
/**
607575
* @ngdoc method
608576
* @name $interval#cancel
@@ -615,17 +583,15 @@ angular.mock.$IntervalProvider = function() {
615583
*/
616584
$interval.cancel = function(promise) {
617585
if (!promise) return false;
618-
var fnIndex;
619-
620-
angular.forEach(repeatFns, function(fn, index) {
621-
if (fn.id === promise.$$intervalId) fnIndex = index;
622-
});
623586

624-
if (angular.isDefined(fnIndex)) {
625-
repeatFns[fnIndex].deferred.promise.then(undefined, function() {});
626-
repeatFns[fnIndex].deferred.reject('canceled');
627-
repeatFns.splice(fnIndex, 1);
628-
return true;
587+
for (var fnIndex = repeatFns.length - 1; fnIndex >= 0; fnIndex--) {
588+
if (repeatFns[fnIndex].id === promise.$$intervalId) {
589+
var deferred = repeatFns[fnIndex].deferred;
590+
deferred.promise.then(undefined, function() {});
591+
deferred.reject('canceled');
592+
repeatFns.splice(fnIndex, 1);
593+
return true;
594+
}
629595
}
630596

631597
return false;

0 commit comments

Comments
 (0)