fix(zone.js): disable wrap uncaught promise rejection should handle primitive value (#38476)

Close #38334.

zone.js provides a flag DISABLE_WRAPPING_UNCAUGHT_PROMISE_REJECTION to let zone.js
throw the original error instead of wrap it when uncaught promise rejection found.
But the rejection value could be anything includes primitive value such as number.
In that case, we should not attach any additional properties to the value.

PR Close #38476
This commit is contained in:
JiaLiPassion 2020-08-15 16:34:22 +09:00 committed by Alex Rickabaugh
parent f979914d4e
commit 19d543f71e
3 changed files with 44 additions and 17 deletions

View File

@ -1,5 +1,3 @@
import {patchMethod} from './utils';
/** /**
* @license * @license
* Copyright Google LLC All Rights Reserved. * Copyright Google LLC All Rights Reserved.
@ -7,6 +5,8 @@ import {patchMethod} from './utils';
* Use of this source code is governed by an MIT-style license that can be * Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {patchMethod} from './utils';
Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePrivate) => { Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
const ObjectDefineProperty = Object.defineProperty; const ObjectDefineProperty = Object.defineProperty;
@ -48,6 +48,9 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
const uncaughtPromiseError: UncaughtPromiseError = _uncaughtPromiseErrors.shift()!; const uncaughtPromiseError: UncaughtPromiseError = _uncaughtPromiseErrors.shift()!;
try { try {
uncaughtPromiseError.zone.runGuarded(() => { uncaughtPromiseError.zone.runGuarded(() => {
if (uncaughtPromiseError.throwOriginal) {
throw uncaughtPromiseError.rejection;
}
throw uncaughtPromiseError; throw uncaughtPromiseError;
}); });
} catch (error) { } catch (error) {
@ -191,10 +194,6 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
if (queue.length == 0 && state == REJECTED) { if (queue.length == 0 && state == REJECTED) {
(promise as any)[symbolState] = REJECTED_NO_CATCH; (promise as any)[symbolState] = REJECTED_NO_CATCH;
let uncaughtPromiseError = value; let uncaughtPromiseError = value;
if (!isDisableWrappingUncaughtPromiseRejection) {
// If disable wrapping uncaught promise reject
// and the rejected value is an Error object,
// use the value instead of wrapping it.
try { try {
// Here we throws a new Error to print more readable error log // Here we throws a new Error to print more readable error log
// and if the value is not an error, zone.js builds an `Error` // and if the value is not an error, zone.js builds an `Error`
@ -205,6 +204,10 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
} catch (err) { } catch (err) {
uncaughtPromiseError = err; uncaughtPromiseError = err;
} }
if (isDisableWrappingUncaughtPromiseRejection) {
// If disable wrapping uncaught promise reject
// use the value instead of wrapping it.
uncaughtPromiseError.throwOriginal = true;
} }
uncaughtPromiseError.rejection = value; uncaughtPromiseError.rejection = value;
uncaughtPromiseError.promise = promise; uncaughtPromiseError.promise = promise;

View File

@ -383,6 +383,7 @@ interface UncaughtPromiseError extends Error {
task: Task; task: Task;
promise: Promise<any>; promise: Promise<any>;
rejection: any; rejection: any;
throwOriginal?: boolean;
} }
/** /**

View File

@ -42,9 +42,9 @@ describe('disable wrap uncaught promise rejection', () => {
setTimeout((): any => null); setTimeout((): any => null);
setTimeout(() => { setTimeout(() => {
expect(promiseError).toBe(error); expect(promiseError).toBe(error);
expect((promiseError as any)['rejection']).toBe(error); expect((promiseError as any)['rejection']).toBe(undefined);
expect((promiseError as any)['zone']).toBe(zone); expect((promiseError as any)['zone']).toBe(undefined);
expect((promiseError as any)['task']).toBe(task); expect((promiseError as any)['task']).toBe(undefined);
done(); done();
}); });
}); });
@ -76,4 +76,27 @@ describe('disable wrap uncaught promise rejection', () => {
done(); done();
}); });
}); });
it('should print original information when a primitive value is used for rejection', (done) => {
let promiseError: number|null = null;
Zone.current
.fork({
name: 'promise-error',
onHandleError: (delegate: ZoneDelegate, current: Zone, target: Zone, error: any):
boolean => {
promiseError = error;
delegate.handleError(target, error);
return false;
}
})
.run(() => {
Promise.reject(42);
expect(promiseError).toBe(null);
});
setTimeout((): any => null);
setTimeout(() => {
expect(promiseError).toBe(42);
done();
});
});
}); });