-
Notifications
You must be signed in to change notification settings - Fork 27.4k
Promise returned from $timeout should not be chained because $$timeoutId is lost #16424
Comments
Good catch, @abobwhite! I think this is a reasonable expectation. We could either use a Lines 320 to 331 in a8830d2
Since it will only affect promises returned by |
Thanks @gkalpak Let me know if I can be of any service. |
@abobwhite, if you fancy taking a stab at it, PRs are always welcome 😉 |
We are going to throw an error if you pass a promise to This requires a breaking change, so will go in 1.7.0. |
@petebacondarwin Thanks for the heads up. The proposed solution seems like a good compromise given the EOL timeline for Angular.js and the (likely) pervasiveness of a change to passing along the |
Previously, calling `$timeout.cancel()` with a promise that was not generated by a call to `$timeout()` 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 `$timeout.cancel()`. With this commit, `$timeout.cancel()` will throw an error if called with a non-$timeout promise, thus surfacing errors that would otherwise go unnoticed. Fixes angular#16424 BREAKING CHNAGE: `$timeout.cancel()` will throw an error if called with a promise that was not generated by `$timeout()`. Previously, it would silently do nothing. Before: ```js var promise = $timeout(doSomething, 1000).then(doSomethingElse); $timeout.cancel(promise); // No error; timeout NOT canceled. ``` After: ```js var promise = $timeout(doSomething, 1000).then(doSomethingElse); $timeout.cancel(promise); // Throws error. ``` Correct usage: ```js var promise = $timeout(doSomething, 1000); var newPromise = promise.then(doSomethingElse); $timeout.cancel(promise); // Timeout canceled. ```
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 angular#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. ```
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 angular#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. ```
Previously, calling `$timeout.cancel()` with a promise that was not generated by a call to `$timeout()` 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 `$timeout.cancel()`. With this commit, `$timeout.cancel()` will throw an error if called with a non-$timeout promise, thus surfacing errors that would otherwise go unnoticed. Fixes angular#16424 BREAKING CHNAGE: `$timeout.cancel()` will throw an error if called with a promise that was not generated by `$timeout()`. Previously, it would silently do nothing. Before: ```js var promise = $timeout(doSomething, 1000).then(doSomethingElse); $timeout.cancel(promise); // No error; timeout NOT canceled. ``` After: ```js var promise = $timeout(doSomething, 1000).then(doSomethingElse); $timeout.cancel(promise); // Throws error. ``` Correct usage: ```js var promise = $timeout(doSomething, 1000); var newPromise = promise.then(doSomethingElse); $timeout.cancel(promise); // Timeout canceled. ```
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 angular#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. ```
Previously, calling `$timeout.cancel()` with a promise that was not generated by a call to `$timeout()` 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 `$timeout.cancel()`. With this commit, `$timeout.cancel()` will throw an error if called with a non-$timeout promise, thus surfacing errors that would otherwise go unnoticed. Fixes angular#16424 BREAKING CHNAGE: `$timeout.cancel()` will throw an error if called with a promise that was not generated by `$timeout()`. Previously, it would silently do nothing. Before: ```js var promise = $timeout(doSomething, 1000).then(doSomethingElse); $timeout.cancel(promise); // No error; timeout NOT canceled. ``` After: ```js var promise = $timeout(doSomething, 1000).then(doSomethingElse); $timeout.cancel(promise); // Throws error. ``` Correct usage: ```js var promise = $timeout(doSomething, 1000); var newPromise = promise.then(doSomethingElse); $timeout.cancel(promise); // Timeout canceled. ```
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 angular#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. ```
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
I'm submitting a ...
Current behavior:
$timeout
returns apromise
that has been "decorated" with a$$timeoutId
that is then required when attempting to cancel the timeout via$timeout.cancel(promise)
. When chaining to this promise upon creation (i.e.then
,catch
, and/orfinally
), the promise returned from the chained function(s) is a new promise that does not contain$$timeoutId
.Expected / new behavior:
Ideally, Angular.js would not be adding properties willy-nilly to this promise. Secondly, it would be great if the chained calls would return back any extra properties set on the original promise (i.e.
$$timeoutId
). BUT, it would suffice if the documentation for$timeout
could be enhanced to provide a note about this limitation.Minimal reproduction of the problem with instructions:
Plnkr - Take note that this is built with a chained
catch
and causes the timeout to not be canceled. If you remove the chainedcatch
or move thecatch
to a separate line after initial assignment, it cancels just fine.AngularJS version:
1.6.6
Browser:
All
Anything else:
I strongly would consider this a defect but because I don't think you're going to change your underlying
then
,catch
, andfinally
implementation to copy any ole' properties from the chained promise, documentation would suffice for any other future souls.The guilty line of code is here: https://github.com/angular/angular.js/blob/master/src/ng/timeout.js#L66
The text was updated successfully, but these errors were encountered: