-
Notifications
You must be signed in to change notification settings - Fork 27.3k
Throwing from $exceptionHandler breaks the digest loop leaving it in a zombie state. #14704
Description
Do you want to request a feature or report a bug?
Bug
What is the current behavior?
When the $exceptionHandler is overridden to throw the thrown exceptions, then the digest loop is sometimes left in a zombie state (i.e. the clearPhase(); is not called).
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (template: http://plnkr.co/edit/tpl:yBpEi4).
I tried to create a minimal demo of the bug, but I was unable to reproduce it.
What is the expected behavior?
The digest loop is operational after the exceptions are thrown (i.e. the clearPhase(); is called before leaving the loop).
What is the motivation / use case for changing the behavior?
I'd like the javascript Errors to be thrown instead of being logged to the console. I have a custom error handler set up on the window.onerror event. Errors don't trigger this event, when thrown from angular context (they are logged to the console instead, as per default $exceptionHandler behaviour)
Which versions of Angular, and which browser / OS are affected by this issue? Did this work in previous versions of Angular? Please also test with the latest stable and snapshot (https://code.angularjs.org/snapshot/) versions.
Tested against version: AngularJS v1.5.7-build.4839+sha.a478f69
Other information (e.g. stacktraces, related issues, suggestions how to fix)
// angular.js
$apply: function(expr) {
try {
beginPhase('$apply');
try {
// (1)
return this.$eval(expr);
} finally {
clearPhase();
}
} catch (e) {
$exceptionHandler(e);
} finally {
try {
// (2)
$rootScope.$digest();
} catch (e) {
$exceptionHandler(e);
throw e;
} finally {
// (3)
// clearPhase();
}
}
},If I add a finally block with a clearPhase(); at position (3), then the digest loop is properly cleaned up in a case of an error being thrown. I tried to create a small demo of this problem, but somehow the demo code throws from the this.$eval(expr)(1), while the code I'm working on is throwing from the $rootScope.$digest()(2). As you can see, when errors are thrown from the (1), then the clearPhase(); is called.
This is probably related with these two PRs:
The second PR would probably fix the problem, because it adds a giant try..finally block in the .$digest() method. It wasn't merged though.
For now, I'm throwing Errors from my $exceptionHandler using a setTimeout() function, so Angular has an opportunity to get out of the digest loop with a proper clean up.