diff --git a/packages/zone.js/lib/common/promise.ts b/packages/zone.js/lib/common/promise.ts index 6f4b59ff80..9103709ac2 100644 --- a/packages/zone.js/lib/common/promise.ts +++ b/packages/zone.js/lib/common/promise.ts @@ -1,5 +1,3 @@ -import {patchMethod} from './utils'; - /** * @license * 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 * found in the LICENSE file at https://angular.io/license */ +import {patchMethod} from './utils'; + Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePrivate) => { const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; const ObjectDefineProperty = Object.defineProperty; @@ -48,6 +48,9 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr const uncaughtPromiseError: UncaughtPromiseError = _uncaughtPromiseErrors.shift()!; try { uncaughtPromiseError.zone.runGuarded(() => { + if (uncaughtPromiseError.throwOriginal) { + throw uncaughtPromiseError.rejection; + } throw uncaughtPromiseError; }); } catch (error) { @@ -191,20 +194,20 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr if (queue.length == 0 && state == REJECTED) { (promise as any)[symbolState] = REJECTED_NO_CATCH; let uncaughtPromiseError = value; - if (!isDisableWrappingUncaughtPromiseRejection) { + try { + // 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` + // Object here to attach the stack information. + throw new Error( + 'Uncaught (in promise): ' + readableObjectToString(value) + + (value && value.stack ? '\n' + value.stack : '')); + } catch (err) { + uncaughtPromiseError = err; + } + if (isDisableWrappingUncaughtPromiseRejection) { // If disable wrapping uncaught promise reject - // and the rejected value is an Error object, // use the value instead of wrapping it. - try { - // 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` - // Object here to attach the stack information. - throw new Error( - 'Uncaught (in promise): ' + readableObjectToString(value) + - (value && value.stack ? '\n' + value.stack : '')); - } catch (err) { - uncaughtPromiseError = err; - } + uncaughtPromiseError.throwOriginal = true; } uncaughtPromiseError.rejection = value; uncaughtPromiseError.promise = promise; diff --git a/packages/zone.js/lib/zone.ts b/packages/zone.js/lib/zone.ts index 54c188b5a7..86b0949bf3 100644 --- a/packages/zone.js/lib/zone.ts +++ b/packages/zone.js/lib/zone.ts @@ -383,6 +383,7 @@ interface UncaughtPromiseError extends Error { task: Task; promise: Promise; rejection: any; + throwOriginal?: boolean; } /** diff --git a/packages/zone.js/test/common/promise-disable-wrap-uncaught-promise-rejection.spec.ts b/packages/zone.js/test/common/promise-disable-wrap-uncaught-promise-rejection.spec.ts index 2906b7d20e..c6f88f265e 100644 --- a/packages/zone.js/test/common/promise-disable-wrap-uncaught-promise-rejection.spec.ts +++ b/packages/zone.js/test/common/promise-disable-wrap-uncaught-promise-rejection.spec.ts @@ -42,9 +42,9 @@ describe('disable wrap uncaught promise rejection', () => { setTimeout((): any => null); setTimeout(() => { expect(promiseError).toBe(error); - expect((promiseError as any)['rejection']).toBe(error); - expect((promiseError as any)['zone']).toBe(zone); - expect((promiseError as any)['task']).toBe(task); + expect((promiseError as any)['rejection']).toBe(undefined); + expect((promiseError as any)['zone']).toBe(undefined); + expect((promiseError as any)['task']).toBe(undefined); done(); }); }); @@ -76,4 +76,27 @@ describe('disable wrap uncaught promise rejection', () => { 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(); + }); + }); });