mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-06-18 20:16:22 +08:00
fix($timeout/$interval): if invokeApply is false, do not use evalAsync
$evalAsync triggers a digest, and is unsuitable when it is expected that a digest should not occur.
BREAKING CHANGE
Previously, even if invokeApply was set to false, a $rootScope digest would occur during promise
resolution. This is no longer the case, as promises returned from $timeout and $interval will no
longer trigger $evalAsync (which in turn causes a $digest) if `invokeApply` is false.
Workarounds include manually triggering $scope.$apply(), or returning $q.defer().promise from a
promise callback, and resolving or rejecting it when appropriate.
var interval = $interval(function() {
if (someRequirementFulfilled) {
$interval.cancel(interval);
$scope.$apply();
}
}, 100, 0, false);
or:
var interval = $interval(function (idx) {
// make the magic happen
}, 1000, 10, false);
interval.then(function(idx) {
var deferred = $q.defer();
// do the asynchronous magic --- $evalAsync will cause a digest and cause
// bindings to update.
return deferred.promise;
});
Closes #7999
Closes #7103
This commit is contained in:
@@ -73,6 +73,7 @@
|
||||
$ParseProvider,
|
||||
$RootScopeProvider,
|
||||
$QProvider,
|
||||
$$QProvider,
|
||||
$$SanitizeUriProvider,
|
||||
$SceProvider,
|
||||
$SceDelegateProvider,
|
||||
@@ -222,6 +223,7 @@ function publishExternalAPI(angular){
|
||||
$parse: $ParseProvider,
|
||||
$rootScope: $RootScopeProvider,
|
||||
$q: $QProvider,
|
||||
$$q: $$QProvider,
|
||||
$sce: $SceProvider,
|
||||
$sceDelegate: $SceDelegateProvider,
|
||||
$sniffer: $SnifferProvider,
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
|
||||
function $IntervalProvider() {
|
||||
this.$get = ['$rootScope', '$window', '$q',
|
||||
function($rootScope, $window, $q) {
|
||||
this.$get = ['$rootScope', '$window', '$q', '$$q',
|
||||
function($rootScope, $window, $q, $$q) {
|
||||
var intervals = {};
|
||||
|
||||
|
||||
@@ -133,10 +133,10 @@ function $IntervalProvider() {
|
||||
function interval(fn, delay, count, invokeApply) {
|
||||
var setInterval = $window.setInterval,
|
||||
clearInterval = $window.clearInterval,
|
||||
deferred = $q.defer(),
|
||||
promise = deferred.promise,
|
||||
iteration = 0,
|
||||
skipApply = (isDefined(invokeApply) && !invokeApply);
|
||||
skipApply = (isDefined(invokeApply) && !invokeApply),
|
||||
deferred = (skipApply ? $$q : $q).defer(),
|
||||
promise = deferred.promise;
|
||||
|
||||
count = isDefined(count) ? count : 0;
|
||||
|
||||
|
||||
@@ -173,6 +173,13 @@ function $QProvider() {
|
||||
}];
|
||||
}
|
||||
|
||||
function $$QProvider() {
|
||||
this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
|
||||
return qFactory(function(callback) {
|
||||
$browser.defer(callback);
|
||||
}, $exceptionHandler);
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a promise manager.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
|
||||
function $TimeoutProvider() {
|
||||
this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler',
|
||||
function($rootScope, $browser, $q, $exceptionHandler) {
|
||||
this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
|
||||
function($rootScope, $browser, $q, $$q, $exceptionHandler) {
|
||||
var deferreds = {};
|
||||
|
||||
|
||||
@@ -33,9 +33,9 @@ function $TimeoutProvider() {
|
||||
*
|
||||
*/
|
||||
function timeout(fn, delay, invokeApply) {
|
||||
var deferred = $q.defer(),
|
||||
var skipApply = (isDefined(invokeApply) && !invokeApply),
|
||||
deferred = (skipApply ? $$q : $q).defer(),
|
||||
promise = deferred.promise,
|
||||
skipApply = (isDefined(invokeApply) && !invokeApply),
|
||||
timeoutId;
|
||||
|
||||
timeoutId = $browser.defer(function() {
|
||||
|
||||
@@ -98,6 +98,23 @@ describe('$interval', function() {
|
||||
}));
|
||||
|
||||
|
||||
it('should NOT call $evalAsync or $digest if invokeApply is set to false',
|
||||
inject(function($interval, $rootScope, $window, $timeout) {
|
||||
var evalAsyncSpy = spyOn($rootScope, '$evalAsync').andCallThrough();
|
||||
var digestSpy = spyOn($rootScope, '$digest').andCallThrough();
|
||||
var notifySpy = jasmine.createSpy('notify');
|
||||
|
||||
$interval(notifySpy, 1000, 1, false);
|
||||
|
||||
$window.flush(2000);
|
||||
$timeout.flush(); // flush $browser.defer() timeout
|
||||
|
||||
expect(notifySpy).toHaveBeenCalledOnce();
|
||||
expect(evalAsyncSpy).not.toHaveBeenCalled();
|
||||
expect(digestSpy).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
|
||||
it('should allow you to specify the delay time', inject(function($interval, $window) {
|
||||
var counter = 0;
|
||||
$interval(function() { counter++; }, 123);
|
||||
|
||||
@@ -48,6 +48,22 @@ describe('$timeout', function() {
|
||||
}));
|
||||
|
||||
|
||||
it('should NOT call $evalAsync or $digest if invokeApply is set to false',
|
||||
inject(function($timeout, $rootScope) {
|
||||
var evalAsyncSpy = spyOn($rootScope, '$evalAsync').andCallThrough();
|
||||
var digestSpy = spyOn($rootScope, '$digest').andCallThrough();
|
||||
var fulfilledSpy = jasmine.createSpy('fulfilled');
|
||||
|
||||
$timeout(fulfilledSpy, 1000, false);
|
||||
|
||||
$timeout.flush();
|
||||
|
||||
expect(fulfilledSpy).toHaveBeenCalledOnce();
|
||||
expect(evalAsyncSpy).not.toHaveBeenCalled();
|
||||
expect(digestSpy).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
|
||||
it('should allow you to specify the delay time', inject(function($timeout, $browser) {
|
||||
var defer = spyOn($browser, 'defer');
|
||||
$timeout(noop, 123);
|
||||
|
||||
Reference in New Issue
Block a user