fix(core): invoke error handler outside of the Angular Zone (#18269)
In Node.JS console.log/error/warn functions actually resuls in a socket write which in turn is considered by Zone.js as an async task. This means that if there is any exception during change detection in a platform-server application the error handler will make the Angular Zone unstable which in turn will cause change detection to run on next tick and cause an infinite loop. It is also better to run the error handler outside of the Angular Zone in general on all platforms so that an error in the error handler itself doesn't cause an infinite loop. Fixes #17073, #7774. PR Close #18269
This commit is contained in:

committed by
Miško Hevery

parent
abee785821
commit
7ae7573bc8
@ -231,12 +231,13 @@ export abstract class PlatformRef {
|
||||
abstract get destroyed(): boolean;
|
||||
}
|
||||
|
||||
function _callAndReportToErrorHandler(errorHandler: ErrorHandler, callback: () => any): any {
|
||||
function _callAndReportToErrorHandler(
|
||||
errorHandler: ErrorHandler, ngZone: NgZone, callback: () => any): any {
|
||||
try {
|
||||
const result = callback();
|
||||
if (isPromise(result)) {
|
||||
return result.catch((e: any) => {
|
||||
errorHandler.handleError(e);
|
||||
ngZone.runOutsideAngular(() => errorHandler.handleError(e));
|
||||
// rethrow as the exception handler might not do it
|
||||
throw e;
|
||||
});
|
||||
@ -244,7 +245,7 @@ function _callAndReportToErrorHandler(errorHandler: ErrorHandler, callback: () =
|
||||
|
||||
return result;
|
||||
} catch (e) {
|
||||
errorHandler.handleError(e);
|
||||
ngZone.runOutsideAngular(() => errorHandler.handleError(e));
|
||||
// rethrow as the exception handler might not do it
|
||||
throw e;
|
||||
}
|
||||
@ -299,8 +300,10 @@ export class PlatformRef_ extends PlatformRef {
|
||||
throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?');
|
||||
}
|
||||
moduleRef.onDestroy(() => remove(this._modules, moduleRef));
|
||||
ngZone !.onError.subscribe({next: (error: any) => { exceptionHandler.handleError(error); }});
|
||||
return _callAndReportToErrorHandler(exceptionHandler, () => {
|
||||
ngZone !.runOutsideAngular(
|
||||
() => ngZone !.onError.subscribe(
|
||||
{next: (error: any) => { exceptionHandler.handleError(error); }}));
|
||||
return _callAndReportToErrorHandler(exceptionHandler, ngZone !, () => {
|
||||
const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus);
|
||||
initStatus.runInitializers();
|
||||
return initStatus.donePromise.then(() => {
|
||||
@ -561,7 +564,7 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||
}
|
||||
} catch (e) {
|
||||
// Attention: Don't rethrow as it could cancel subscriptions to Observables!
|
||||
this._exceptionHandler.handleError(e);
|
||||
this._zone.runOutsideAngular(() => this._exceptionHandler.handleError(e));
|
||||
} finally {
|
||||
this._runningTick = false;
|
||||
wtfLeave(scope);
|
||||
|
@ -258,7 +258,7 @@ function forkInnerZoneWithAngularBehavior(zone: NgZonePrivate) {
|
||||
|
||||
onHandleError: (delegate: ZoneDelegate, current: Zone, target: Zone, error: any): boolean => {
|
||||
delegate.handleError(target, error);
|
||||
zone.onError.emit(error);
|
||||
zone.runOutsideAngular(() => zone.onError.emit(error));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
Reference in New Issue
Block a user