Skip to content

Commit 0b2ec50

Browse files
committed
fix($interval): do not invoke $apply when invokeApply is false
This CL allows $q Deferred objects to avoid invoking $rootScope.$digest, by optionally adding asynchronously invoked functions to a different queue which is processed outside of $digest. Closes angular#7103
1 parent 1f6a5a1 commit 0b2ec50

File tree

4 files changed

+49
-16
lines changed

4 files changed

+49
-16
lines changed

src/ng/interval.js

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ function $IntervalProvider() {
138138
iteration = 0,
139139
skipApply = (isDefined(invokeApply) && !invokeApply);
140140

141+
deferred.$$skipApply = skipApply;
141142
count = isDefined(count) ? count : 0;
142143

143144
promise.then(null, null, fn);

src/ng/q.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@
171171
function $QProvider() {
172172

173173
this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
174-
return qFactory(function(callback) {
175-
$rootScope.$evalAsync(callback);
174+
return qFactory(function(callback, skipApply) {
175+
$rootScope.$evalAsync(callback, skipApply);
176176
}, $exceptionHandler);
177177
}];
178178
}
@@ -208,7 +208,7 @@ function qFactory(nextTick, exceptionHandler) {
208208
if (pending) {
209209
var callbacks = pending;
210210
pending = undefined;
211-
value = ref(val);
211+
value = ref(val, deferred.$$skipApply);
212212

213213
if (callbacks.length) {
214214
nextTick(function() {
@@ -217,7 +217,7 @@ function qFactory(nextTick, exceptionHandler) {
217217
callback = callbacks[i];
218218
value.then(callback[0], callback[1], callback[2]);
219219
}
220-
});
220+
}, deferred.$$skipApply);
221221
}
222222
}
223223
},
@@ -239,7 +239,7 @@ function qFactory(nextTick, exceptionHandler) {
239239
callback = callbacks[i];
240240
callback[2](progress);
241241
}
242-
});
242+
}, deferred.$$skipApply);
243243
}
244244
}
245245
},
@@ -331,14 +331,14 @@ function qFactory(nextTick, exceptionHandler) {
331331
};
332332

333333

334-
var ref = function(value) {
334+
var ref = function(value, skipApply) {
335335
if (value && isFunction(value.then)) return value;
336336
return {
337337
then: function(callback) {
338338
var result = defer();
339339
nextTick(function() {
340340
result.resolve(callback(value));
341-
});
341+
}, skipApply);
342342
return result.promise;
343343
}
344344
};

src/ng/rootScope.js

+26-9
Original file line numberDiff line numberDiff line change
@@ -904,18 +904,35 @@ function $RootScopeProvider(){
904904
* - `function(scope)`: execute the function with the current `scope` parameter.
905905
*
906906
*/
907-
$evalAsync: function(expr) {
907+
$evalAsync: function(expr, skipApply) {
908908
// if we are outside of an $digest loop and this is the first time we are scheduling async
909909
// task also schedule async auto-flush
910-
if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) {
911-
$browser.defer(function() {
912-
if ($rootScope.$$asyncQueue.length) {
913-
$rootScope.$digest();
914-
}
915-
});
910+
if (!skipApply) {
911+
if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) {
912+
$browser.defer(function() {
913+
if ($rootScope.$$asyncQueue.length) {
914+
$rootScope.$digest();
915+
}
916+
});
917+
}
918+
this.$$asyncQueue.push({scope: this, expression: expr});
919+
} else {
920+
var asyncQueue = ($rootScope.$$liteAsyncQueue || ($rootScope.$$liteAsyncQueue = []));
921+
var asyncTask;
922+
if (!asyncQueue.length) {
923+
$browser.defer(function() {
924+
while (asyncQueue.length) {
925+
try {
926+
asyncTask = asyncQueue.shift();
927+
asyncTask.scope.$eval(asyncTask.expression);
928+
} catch (e) {
929+
$exceptionHandler(e);
930+
}
931+
}
932+
});
933+
}
934+
asyncQueue.push({scope: this, expression: expr});
916935
}
917-
918-
this.$$asyncQueue.push({scope: this, expression: expr});
919936
},
920937

921938
$$postDigest : function(fn) {

test/ng/intervalSpec.js

+15
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,21 @@ describe('$interval', function() {
9898
}));
9999

100100

101+
it('should NOT call $rootScope.$digest if invokeApply is set to false',
102+
inject(function($interval, $rootScope, $window, $browser) {
103+
var digestSpy = spyOn($rootScope, '$digest').andCallThrough();
104+
var resolveSpy = jasmine.createSpy('resolve');
105+
var notifySpy = jasmine.createSpy('notify');
106+
$interval(noop, 1000, 1, false).then(resolveSpy, null, notifySpy);
107+
108+
$window.flush(4000);
109+
$browser.defer.flush();
110+
expect(digestSpy).not.toHaveBeenCalled();
111+
expect(resolveSpy).toHaveBeenCalledOnce();
112+
expect(notifySpy).toHaveBeenCalledOnce();
113+
}));
114+
115+
101116
it('should allow you to specify the delay time', inject(function($interval, $window) {
102117
var counter = 0;
103118
$interval(function() { counter++; }, 123);

0 commit comments

Comments
 (0)