This repository has been archived by the owner on Apr 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 27.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat($timeout): add $timeout service that supersedes $defer
$timeout has a better name ($defer got often confused with something related to $q) and is actually promise based with cancelation support. With this commit the $defer service is deprecated and will be removed before 1.0. Closes #704, #532
- Loading branch information
Showing
8 changed files
with
284 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
'use strict'; | ||
|
||
|
||
function $TimeoutProvider() { | ||
this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler', | ||
function($rootScope, $browser, $q, $exceptionHandler) { | ||
var deferreds = {}; | ||
|
||
|
||
/** | ||
* @ngdoc function | ||
* @name angular.module.ng.$timeout | ||
* @requires $browser | ||
* | ||
* @description | ||
* Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch | ||
* block and delegates any exceptions to | ||
* {@link angular.module.ng.$exceptionHandler $exceptionHandler} service. | ||
* | ||
* The return value of registering a timeout function is a promise which will be resolved when | ||
* the timeout is reached and the timeout function is executed. | ||
* | ||
* To cancel a the timeout request, call `$timeout.cancel(promise)`. | ||
* | ||
* In tests you can use {@link angular.module.ngMock.$timeout `$timeout.flush()`} to | ||
* synchronously flush the queue of deferred functions. | ||
* | ||
* @param {function()} fn A function, who's execution should be delayed. | ||
* @param {number=} [delay=0] Delay in milliseconds. | ||
* @param {boolean=} [invokeApply=true] If set to false skips model dirty checking, otherwise | ||
* will invoke `fn` within the {@link angular.module.ng.$rootScope.Scope#$apply $apply} block. | ||
* @returns {*} Promise that will be resolved when the timeout is reached. The value this | ||
* promise will be resolved with is the return value of the `fn` function. | ||
*/ | ||
function timeout(fn, delay, invokeApply) { | ||
var deferred = $q.defer(), | ||
promise = deferred.promise, | ||
skipApply = (isDefined(invokeApply) && !invokeApply), | ||
timeoutId, cleanup; | ||
|
||
timeoutId = $browser.defer(function() { | ||
try { | ||
deferred.resolve(fn()); | ||
} catch(e) { | ||
deferred.reject(e); | ||
$exceptionHandler(e); | ||
} | ||
|
||
if (!skipApply) $rootScope.$apply(); | ||
}, delay); | ||
|
||
cleanup = function() { | ||
delete deferreds[promise.$$timeoutId]; | ||
}; | ||
|
||
promise.$$timeoutId = timeoutId; | ||
deferreds[timeoutId] = deferred; | ||
promise.then(cleanup, cleanup); | ||
|
||
return promise; | ||
} | ||
|
||
|
||
/** | ||
* @ngdoc function | ||
* @name angular.module.ng.$timeout#cancel | ||
* @methodOf angular.module.ng.$timeout | ||
* | ||
* @description | ||
* Cancels a task associated with the `promise`. As a result of this the promise will be | ||
* resolved with a rejection. | ||
* | ||
* @param {Promise} promise Promise returned by the `$timeout` function. | ||
* @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully | ||
* canceled. | ||
*/ | ||
timeout.cancel = function(promise) { | ||
if (promise.$$timeoutId in deferreds) { | ||
deferreds[promise.$$timeoutId].reject('canceled'); | ||
return $browser.defer.cancel(promise.$$timeoutId); | ||
} | ||
return false; | ||
}; | ||
|
||
return timeout; | ||
}]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
'use strict'; | ||
|
||
describe('$timeout', function() { | ||
|
||
beforeEach(module(provideLog)); | ||
|
||
|
||
it('should delegate functions to $browser.defer', inject(function($timeout, $browser) { | ||
var counter = 0; | ||
$timeout(function() { counter++; }); | ||
|
||
expect(counter).toBe(0); | ||
|
||
$browser.defer.flush(); | ||
expect(counter).toBe(1); | ||
|
||
expect(function() {$browser.defer.flush();}).toThrow('No deferred tasks to be flushed'); | ||
expect(counter).toBe(1); | ||
})); | ||
|
||
|
||
it('should call $apply after each callback is executed', inject(function($timeout, $rootScope) { | ||
var applySpy = spyOn($rootScope, '$apply').andCallThrough(); | ||
|
||
$timeout(function() {}); | ||
expect(applySpy).not.toHaveBeenCalled(); | ||
|
||
$timeout.flush(); | ||
expect(applySpy).toHaveBeenCalledOnce(); | ||
|
||
applySpy.reset(); | ||
|
||
$timeout(function() {}); | ||
$timeout(function() {}); | ||
$timeout.flush(); | ||
expect(applySpy.callCount).toBe(2); | ||
})); | ||
|
||
|
||
it('should NOT call $apply if skipApply is set to true', inject(function($timeout, $rootScope) { | ||
var applySpy = spyOn($rootScope, '$apply').andCallThrough(); | ||
|
||
$timeout(function() {}, 12, false); | ||
expect(applySpy).not.toHaveBeenCalled(); | ||
|
||
$timeout.flush(); | ||
expect(applySpy).not.toHaveBeenCalled(); | ||
})); | ||
|
||
|
||
it('should allow you to specify the delay time', inject(function($timeout, $browser) { | ||
var defer = spyOn($browser, 'defer'); | ||
$timeout(noop, 123); | ||
expect(defer.callCount).toEqual(1); | ||
expect(defer.mostRecentCall.args[1]).toEqual(123); | ||
})); | ||
|
||
|
||
it('should return a promise which will be resolved with return value of the timeout callback', | ||
inject(function($timeout, log) { | ||
var promise = $timeout(function() { log('timeout'); return 'buba'; }); | ||
|
||
promise.then(function(value) { log('promise success: ' + value); }, log.fn('promise error')); | ||
expect(log).toEqual([]); | ||
|
||
$timeout.flush(); | ||
expect(log).toEqual(['timeout', 'promise success: buba']); | ||
})); | ||
|
||
|
||
describe('exception handling', function() { | ||
|
||
beforeEach(module(function($exceptionHandlerProvider) { | ||
$exceptionHandlerProvider.mode('log'); | ||
})); | ||
|
||
|
||
it('should delegate exception to the $exceptionHandler service', inject( | ||
function($timeout, $exceptionHandler) { | ||
$timeout(function() {throw "Test Error";}); | ||
expect($exceptionHandler.errors).toEqual([]); | ||
|
||
$timeout.flush(); | ||
expect($exceptionHandler.errors).toEqual(["Test Error"]); | ||
})); | ||
|
||
|
||
it('should call $apply even if an exception is thrown in callback', inject( | ||
function($timeout, $rootScope) { | ||
var applySpy = spyOn($rootScope, '$apply').andCallThrough(); | ||
|
||
$timeout(function() {throw "Test Error";}); | ||
expect(applySpy).not.toHaveBeenCalled(); | ||
|
||
$timeout.flush(); | ||
expect(applySpy).toHaveBeenCalled(); | ||
})); | ||
|
||
|
||
it('should reject the timeout promise when an exception is thrown in the timeout callback', | ||
inject(function($timeout, log) { | ||
var promise = $timeout(function() { throw "Some Error"; }); | ||
|
||
promise.then(log.fn('success'), function(reason) { log('error: ' + reason); }); | ||
$timeout.flush(); | ||
|
||
expect(log).toEqual('error: Some Error'); | ||
})); | ||
}); | ||
|
||
|
||
describe('cancel', function() { | ||
it('should cancel tasks', inject(function($timeout) { | ||
var task1 = jasmine.createSpy('task1'), | ||
task2 = jasmine.createSpy('task2'), | ||
task3 = jasmine.createSpy('task3'), | ||
promise1, promise3; | ||
|
||
promise1 = $timeout(task1); | ||
$timeout(task2); | ||
promise3 = $timeout(task3, 333); | ||
|
||
$timeout.cancel(promise3); | ||
$timeout.cancel(promise1); | ||
$timeout.flush(); | ||
|
||
expect(task1).not.toHaveBeenCalled(); | ||
expect(task2).toHaveBeenCalledOnce(); | ||
expect(task3).not.toHaveBeenCalled(); | ||
})); | ||
|
||
|
||
it('should return true if a task was successfully canceled', inject(function($timeout) { | ||
var task1 = jasmine.createSpy('task1'), | ||
task2 = jasmine.createSpy('task2'), | ||
promise1, promise2; | ||
|
||
promise1 = $timeout(task1); | ||
$timeout.flush(); | ||
promise2 = $timeout(task2); | ||
|
||
expect($timeout.cancel(promise1)).toBe(false); | ||
expect($timeout.cancel(promise2)).toBe(true); | ||
})); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters