refactor($interval): share code between $interval and ngMock/$interval

This avoids code/logic duplication and helps the implementations stay
in-sync.
This commit is contained in:
George Kalpakas
2018-06-26 20:45:44 +03:00
parent 1c380b7671
commit af59a0a00b
5 changed files with 98 additions and 116 deletions
+1
View File
@@ -28,6 +28,7 @@ var angularFiles = {
'src/ng/httpBackend.js',
'src/ng/interpolate.js',
'src/ng/interval.js',
'src/ng/intervalFactory.js',
'src/ng/jsonpCallbacks.js',
'src/ng/locale.js',
'src/ng/location.js',
+2
View File
@@ -70,6 +70,7 @@
$FilterProvider,
$$ForceReflowProvider,
$InterpolateProvider,
$$IntervalFactoryProvider,
$IntervalProvider,
$HttpProvider,
$HttpParamSerializerProvider,
@@ -241,6 +242,7 @@ function publishExternalAPI(angular) {
$$forceReflow: $$ForceReflowProvider,
$interpolate: $InterpolateProvider,
$interval: $IntervalProvider,
$$intervalFactory: $$IntervalFactoryProvider,
$http: $HttpProvider,
$httpParamSerializer: $HttpParamSerializerProvider,
$httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
+13 -48
View File
@@ -4,10 +4,18 @@ var $intervalMinErr = minErr('$interval');
/** @this */
function $IntervalProvider() {
this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
function($rootScope, $window, $q, $$q, $browser) {
this.$get = ['$$intervalFactory', '$window',
function($$intervalFactory, $window) {
var intervals = {};
var setIntervalFn = function(tick, delay, deferred) {
var id = $window.setInterval(tick, delay);
intervals[id] = deferred;
return id;
};
var clearIntervalFn = function(id) {
$window.clearInterval(id);
delete intervals[id];
};
/**
* @ngdoc service
@@ -135,49 +143,7 @@ function $IntervalProvider() {
* </file>
* </example>
*/
function interval(fn, delay, count, invokeApply) {
var hasParams = arguments.length > 4,
args = hasParams ? sliceArgs(arguments, 4) : [],
setInterval = $window.setInterval,
clearInterval = $window.clearInterval,
iteration = 0,
skipApply = (isDefined(invokeApply) && !invokeApply),
deferred = (skipApply ? $$q : $q).defer(),
promise = deferred.promise;
count = isDefined(count) ? count : 0;
promise.$$intervalId = setInterval(function tick() {
if (skipApply) {
$browser.defer(callback);
} else {
$rootScope.$evalAsync(callback);
}
deferred.notify(iteration++);
if (count > 0 && iteration >= count) {
deferred.resolve(iteration);
clearInterval(promise.$$intervalId);
delete intervals[promise.$$intervalId];
}
if (!skipApply) $rootScope.$apply();
}, delay);
intervals[promise.$$intervalId] = deferred;
return promise;
function callback() {
if (!hasParams) {
fn(iteration);
} else {
fn.apply(null, args);
}
}
}
var interval = $$intervalFactory(setIntervalFn, clearIntervalFn);
/**
* @ngdoc method
@@ -205,8 +171,7 @@ function $IntervalProvider() {
// Interval cancels should not report an unhandled promise.
markQExceptionHandled(deferred.promise);
deferred.reject('canceled');
$window.clearInterval(id);
delete intervals[id];
clearIntervalFn(id);
return true;
};
+48
View File
@@ -0,0 +1,48 @@
'use strict';
/** @this */
function $$IntervalFactoryProvider() {
this.$get = ['$browser', '$q', '$$q', '$rootScope',
function($browser, $q, $$q, $rootScope) {
return function intervalFactory(setIntervalFn, clearIntervalFn) {
return function intervalFn(fn, delay, count, invokeApply) {
var hasParams = arguments.length > 4,
args = hasParams ? sliceArgs(arguments, 4) : [],
iteration = 0,
skipApply = isDefined(invokeApply) && !invokeApply,
deferred = (skipApply ? $$q : $q).defer(),
promise = deferred.promise;
count = isDefined(count) ? count : 0;
function callback() {
if (!hasParams) {
fn(iteration);
} else {
fn.apply(null, args);
}
}
function tick() {
if (skipApply) {
$browser.defer(callback);
} else {
$rootScope.$evalAsync(callback);
}
deferred.notify(iteration++);
if (count > 0 && iteration >= count) {
deferred.resolve(iteration);
clearIntervalFn(promise.$$intervalId);
}
if (!skipApply) $rootScope.$apply();
}
promise.$$intervalId = setIntervalFn(tick, delay, deferred, skipApply);
return promise;
};
};
}];
}
+34 -68
View File
@@ -537,72 +537,40 @@ angular.mock.$LogProvider = function() {
* @returns {promise} A promise which will be notified on each iteration.
*/
angular.mock.$IntervalProvider = function() {
this.$get = ['$browser', '$rootScope', '$q', '$$q',
function($browser, $rootScope, $q, $$q) {
this.$get = ['$browser', '$$intervalFactory',
function($browser, $$intervalFactory) {
var repeatFns = [],
nextRepeatId = 0,
now = 0;
now = 0,
setIntervalFn = function(tick, delay, deferred, skipApply) {
var id = nextRepeatId++;
var fn = !skipApply ? tick : function() {
tick();
$browser.defer.flush();
};
var $interval = function(fn, delay, count, invokeApply) {
var hasParams = arguments.length > 4,
args = hasParams ? Array.prototype.slice.call(arguments, 4) : [],
iteration = 0,
skipApply = (angular.isDefined(invokeApply) && !invokeApply),
deferred = (skipApply ? $$q : $q).defer(),
promise = deferred.promise;
count = (angular.isDefined(count)) ? count : 0;
promise.$$intervalId = nextRepeatId;
function tick() {
if (skipApply) {
$browser.defer(callback);
} else {
$rootScope.$evalAsync(callback);
}
deferred.notify(iteration++);
if (count > 0 && iteration >= count) {
var fnIndex;
deferred.resolve(iteration);
angular.forEach(repeatFns, function(fn, index) {
if (fn.id === promise.$$intervalId) fnIndex = index;
repeatFns.push({
nextTime: (now + (delay || 0)),
delay: delay || 1,
fn: fn,
id: id,
deferred: deferred
});
repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime; });
if (angular.isDefined(fnIndex)) {
repeatFns.splice(fnIndex, 1);
return id;
},
clearIntervalFn = function(id) {
for (var fnIndex = repeatFns.length - 1; fnIndex >= 0; fnIndex--) {
if (repeatFns[fnIndex].id === id) {
repeatFns.splice(fnIndex, 1);
break;
}
}
}
};
if (skipApply) {
$browser.defer.flush();
} else {
$rootScope.$apply();
}
}
var $interval = $$intervalFactory(setIntervalFn, clearIntervalFn);
function callback() {
if (!hasParams) {
fn(iteration);
} else {
fn.apply(null, args);
}
}
repeatFns.push({
nextTime: (now + (delay || 0)),
delay: delay || 1,
fn: tick,
id: nextRepeatId,
deferred: deferred
});
repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
nextRepeatId++;
return promise;
};
/**
* @ngdoc method
* @name $interval#cancel
@@ -615,17 +583,15 @@ angular.mock.$IntervalProvider = function() {
*/
$interval.cancel = function(promise) {
if (!promise) return false;
var fnIndex;
angular.forEach(repeatFns, function(fn, index) {
if (fn.id === promise.$$intervalId) fnIndex = index;
});
if (angular.isDefined(fnIndex)) {
repeatFns[fnIndex].deferred.promise.then(undefined, function() {});
repeatFns[fnIndex].deferred.reject('canceled');
repeatFns.splice(fnIndex, 1);
return true;
for (var fnIndex = repeatFns.length - 1; fnIndex >= 0; fnIndex--) {
if (repeatFns[fnIndex].id === promise.$$intervalId) {
var deferred = repeatFns[fnIndex].deferred;
deferred.promise.then(undefined, function() {});
deferred.reject('canceled');
repeatFns.splice(fnIndex, 1);
return true;
}
}
return false;