Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit a8bef95

Browse files
committed
fix($interval): throw when trying to cancel non-$interval promise
Previously, calling `$interval.cancel()` with a promise that was not generated by a call to `$interval()` would do nothing. This could, for example, happen when calling `.then()`/`.catch()` on the returned promise, which creates a new promise, and passing that to `$interval.cancel()`. With this commit, `$interval.cancel()` will throw an error if called with a non-$interval promise, thus surfacing errors that would otherwise go unnoticed. Related to #16424. BREAKING CHNAGE: `$interval.cancel()` will throw an error if called with a promise that was not generated by `$interval()`. Previously, it would silently do nothing. Before: ```js var promise = $interval(doSomething, 1000, 5).then(doSomethingElse); $interval.cancel(promise); // No error; interval NOT canceled. ``` After: ```js var promise = $interval(doSomething, 1000, 5).then(doSomethingElse); $interval.cancel(promise); // Throws error. ``` Correct usage: ```js var promise = $interval(doSomething, 1000, 5); var newPromise = promise.then(doSomethingElse); $interval.cancel(promise); // Interval canceled. ``` Closes #16476
1 parent 3365256 commit a8bef95

File tree

3 files changed

+52
-10
lines changed

3 files changed

+52
-10
lines changed
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
@ngdoc error
2+
@name $interval:badprom
3+
@fullName Non-$interval promise
4+
@description
5+
6+
This error occurs when calling {@link ng.$interval#cancel $interval.cancel()} with a promise that
7+
was not generated by the {@link ng.$interval $interval} service. This can, for example, happen when
8+
calling {@link ng.$q#the-promise-api then()/catch()} on the returned promise, which creates a new
9+
promise, and pass that new promise to {@link ng.$interval#cancel $interval.cancel()}.
10+
11+
Example of incorrect usage that leads to this error:
12+
13+
```js
14+
var promise = $interval(doSomething, 1000, 5).then(doSomethingElse);
15+
$interval.cancel(promise);
16+
```
17+
18+
To fix the example above, keep a reference to the promise returned by
19+
{@link ng.$interval $interval()} and pass that to {@link ng.$interval#cancel $interval.cancel()}:
20+
21+
```js
22+
var promise = $interval(doSomething, 1000, 5);
23+
var newPromise = promise.then(doSomethingElse);
24+
$interval.cancel(promise);
25+
```

src/ng/interval.js

+20-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22

3+
var $intervalMinErr = minErr('$interval');
4+
35
/** @this */
46
function $IntervalProvider() {
57
this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
@@ -188,15 +190,25 @@ function $IntervalProvider() {
188190
* @returns {boolean} Returns `true` if the task was successfully canceled.
189191
*/
190192
interval.cancel = function(promise) {
191-
if (promise && promise.$$intervalId in intervals) {
192-
// Interval cancels should not report as unhandled promise.
193-
markQExceptionHandled(intervals[promise.$$intervalId].promise);
194-
intervals[promise.$$intervalId].reject('canceled');
195-
$window.clearInterval(promise.$$intervalId);
196-
delete intervals[promise.$$intervalId];
197-
return true;
193+
if (!promise) return false;
194+
195+
if (!promise.hasOwnProperty('$$intervalId')) {
196+
throw $intervalMinErr('badprom',
197+
'`$interval.cancel()` called with a promise that was not generated by `$interval()`.');
198198
}
199-
return false;
199+
200+
if (!intervals.hasOwnProperty(promise.$$intervalId)) return false;
201+
202+
var id = promise.$$intervalId;
203+
var deferred = intervals[id];
204+
205+
// Interval cancels should not report an unhandled promise.
206+
markQExceptionHandled(deferred.promise);
207+
deferred.reject('canceled');
208+
$window.clearInterval(id);
209+
delete intervals[id];
210+
211+
return true;
200212
};
201213

202214
return interval;

test/ng/intervalSpec.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -335,12 +335,17 @@ describe('$interval', function() {
335335
}));
336336

337337

338-
it('should not throw a runtime exception when given an undefined promise',
339-
inject(function($interval) {
338+
it('should not throw an error when given an undefined promise', inject(function($interval) {
340339
expect($interval.cancel()).toBe(false);
341340
}));
342341

343342

343+
it('should throw an error when given a non-$interval promise', inject(function($interval) {
344+
var promise = $interval(noop).then(noop);
345+
expect(function() { $interval.cancel(promise); }).toThrowMinErr('$interval', 'badprom');
346+
}));
347+
348+
344349
it('should not trigger digest when cancelled', inject(function($interval, $rootScope, $browser) {
345350
var watchSpy = jasmine.createSpy('watchSpy');
346351
$rootScope.$watch(watchSpy);

0 commit comments

Comments
 (0)