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

feat($q): report promises with non rejection callback #13662

Closed
wants to merge 2 commits into from

Conversation

lgalfaso
Copy link
Contributor

@lgalfaso lgalfaso commented Jan 2, 2016

Rejected promises that do not have a callback to handle the rejection report this to $exceptionHandler so they can be logged to the console.

BREAKING CHANGE

Tests that depend on specific order or number of messages in $exceptionHandler
will need to handle rejected promises report.

Closes: #13653
Closes: #7992

@@ -702,12 +702,13 @@ angular.module('ngResource', ['ng']).
return $q.reject(response);
});

promise.finally(function() {
promise = promise.finally(function(response) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .finally callback doesn't take any arguments and the result is ignored unless it's a promise (and even when it's a promise the result of that promise is ignored). I think this arg and the return can be dropped.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fact: If a resource call is canceled the internal promise is rejected

The previous code did the following:

internalPromise.finally(...) this creates a promise that I will call X
internalPromise.then(...) this second promise is returned. I will call this promise Y

If internalPromise is rejected, then it will reject X and Y. The user can handle the rejection of Y as this is what they receive, but they can do nothing about X. This leaves us with 2 options

1/ Change the code to

promise.finally(...).then(noop, noop);
promise = promise.then(...);

This will handle any rejections from X

2/ What is in this patch

promise = promise.finally(...).then(...);

The second option has the benefit of not creating another promise

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That all makes sense... but response is undefined 100% of the time because the .finally callback doesn't take any arguments. You can drop the response arg and return statement...

@lgalfaso
Copy link
Contributor Author

@gkalpak added an option to disable this behavior. PTAL.

* @kind function
*
* @description
* Retrieves or overrides on wheter to generate an error when a rejected promise is not handled.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on wheter --> whether (?)

@gkalpak
Copy link
Member

gkalpak commented Mar 21, 2016

I left some comments. Some additional thoughts:

  1. If we have this enabled by default, shouldn't we also document the extra errors as a BC.
    E.g. if my otherwise perfect production application started logging warnings in my clients console, I would like a big, bold heads-up, so I can take appropriate meassures.
  2. Same as with $timeout/$interval, an explicitly cancelled $resource request (using $cancelRequest) should not report an error, imo.
  3. To avoid surprises, we should be more explicit - both in the docs and in tests - about cases that we are handling internally (e.g. $timeout/$interval#cancel(), $resource#$cancelRequest()?).

@lgalfaso lgalfaso force-pushed the q-issue-13653 branch 2 times, most recently from e812b71 to d18ff2e Compare March 21, 2016 21:36
@lgalfaso
Copy link
Contributor Author

Updated the PR with the changes. Made the change to $resource#$cancelRequest() so it follows the same logic that is present for $timeout/$interval#cancel().

That said, I do not think that there is a need to add any special documentation for this case.

@lgalfaso lgalfaso force-pushed the q-issue-13653 branch 2 times, most recently from a85c87f to f03988c Compare March 21, 2016 21:42

/**
* @ngdoc method
* @name $qProvider#errorOnUnhandledRejections
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder where this method shows up in the docs (considering there are no docs for $qProvider).
I think there should "standalone" docs for $qProvider (even if just containing this method), but it can be tackled in a follow-up PR.

@gkalpak
Copy link
Member

gkalpak commented Mar 22, 2016

I left a couple of minor comments, but it LGTM otherwise.

I'm not sure about the default value of the flag. I guess true is OK although it might scare people at first when they migrate to 1.6.x (I don't think anyone is handling all their rejections in tests for example).
Maybe we could even backport it to 1.5.x with the flag set to false by default (but it's not necessary).

@lgalfaso
Copy link
Contributor Author

Added a page for qProvider, fixed the documentation issue and changed the remaining .then(noop, noop) to catch(noop)

@lgalfaso
Copy link
Contributor Author

About backporting this to 1.5. I am fine as long as the default value is false. This ways, people can check this before 1.6 is out.

Rejected promises that do not have a callback to handle the rejection report
this to $exceptionHandler so they can be logged to the console.

BREAKING CHANGE

Unhandled rejected promises will be logged to $exceptionHandler.

Tests that depend on specific order or number of messages in $exceptionHandler
will need to handle rejected promises report.

Closes: angular#13653
Closes: angular#7992
@lgalfaso
Copy link
Contributor Author

landed into master as c9dffde and 71cf28c

@lgalfaso lgalfaso closed this Mar 27, 2016
stefan-mihaila added a commit to balena-io/etcher that referenced this pull request Feb 7, 2017
Angular 1.5 has a bug that causes angular's `$exceptionHandler`
to be called for rejected `$q` promises even if they have
a rejection handler. This bug caused duplicate error messages
in Etcher.

A consequence of upgrading to Angular 1.6 is that `$q` promises
without a rejection handler will throw `Possibly unhandled rejection`
errors. To avoid these errors, this commit moves code responsible
for opening a tooltip from the template to the controller and handles
the rejection.

Other packages upgraded:
- angular-moment to v1.0.1
- angular-ui-router to v0.4.2
- angular-mocks to v1.6.1

Change-type: patch
Changelog-Entry: Fix duplicate error messages
Fixes: #1082
See: angular/angular.js#7992
See: angular/angular.js#13662
stefan-mihaila added a commit to balena-io/etcher that referenced this pull request Feb 7, 2017
Angular 1.5 has a bug that causes angular's `$exceptionHandler`
to be called for rejected `$q` promises even if they have
a rejection handler. This bug caused duplicate error messages
in Etcher.

A consequence of upgrading to Angular 1.6 is that `$q` promises
without a rejection handler will throw `Possibly unhandled rejection`
errors. To avoid these errors, this commit moves code responsible
for opening a tooltip from the template to the controller and handles
the rejection.

Other packages upgraded:
- angular-moment to v1.0.1
- angular-ui-router to v0.4.2
- angular-mocks to v1.6.1

Change-type: patch
Changelog-Entry: Fix duplicate error messages
Fixes: #1082
See: angular/angular.js#7992
See: angular/angular.js#13662
jviotti pushed a commit to balena-io/etcher that referenced this pull request Feb 7, 2017
Angular 1.5 has a bug that causes angular's `$exceptionHandler`
to be called for rejected `$q` promises even if they have
a rejection handler. This bug caused duplicate error messages
in Etcher.

A consequence of upgrading to Angular 1.6 is that `$q` promises
without a rejection handler will throw `Possibly unhandled rejection`
errors. To avoid these errors, this commit moves code responsible
for opening a tooltip from the template to the controller and handles
the rejection.

Other packages upgraded:
- angular-moment to v1.0.1
- angular-ui-router to v0.4.2
- angular-mocks to v1.6.1

Change-type: patch
Changelog-Entry: Fix duplicate error messages
Fixes: #1082
See: angular/angular.js#7992
See: angular/angular.js#13662
if (!toCheck.pur) {
toCheck.pur = true;
var errorMessage = 'Possibly unhandled rejection: ' + toDebugString(toCheck.value);
exceptionHandler(errorMessage);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why errorMessage isn't wrapped by new Error() so that stack trace is available?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@amalitsky This code path is only used if the value thrown in a promise callback was not an error. We could construct an error object here but it wouldn't be of much value as its stack would end up here and not in the place from where the error comes.

To be useful, only errors need to be thrown in promise callbacks and not other values. Which is a good idea in general.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mgol, wouldn't path also have a reference to the promise invocation?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants