diff --git a/lib/common/promise.ts b/lib/common/promise.ts index f2d4e6c98..f2df7767c 100644 --- a/lib/common/promise.ts +++ b/lib/common/promise.ts @@ -6,6 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePrivate) => { + function readableObjectToString(obj: any) { + if (obj && obj.toString === Object.prototype.toString) { + const className = obj.constructor && obj.constructor.name; + return (className ? className : '') + ': ' + JSON.stringify(obj); + } + + return obj ? obj.toString() : Object.prototype.toString.call(obj); + } + interface UncaughtPromiseError extends Error { zone: AmbientZone; task: Task; @@ -164,8 +173,9 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr if (queue.length == 0 && state == REJECTED) { (promise as any)[symbolState] = REJECTED_NO_CATCH; try { + // try to print more readable error log throw new Error( - 'Uncaught (in promise): ' + value + + 'Uncaught (in promise): ' + readableObjectToString(value) + (value && value.stack ? '\n' + value.stack : '')); } catch (err) { const error: UncaughtPromiseError = err; diff --git a/test/common/Promise.spec.ts b/test/common/Promise.spec.ts index 00c4db71f..6f2ea565f 100644 --- a/test/common/Promise.spec.ts +++ b/test/common/Promise.spec.ts @@ -33,6 +33,11 @@ function flushMicrotasks() { Zone.current.get('flush')(); } +class TestRejection { + prop1: string; + prop2: string; +} + describe( 'Promise', ifEnvSupports('Promise', function() { if (!global.Promise) return; @@ -344,6 +349,38 @@ describe( done(); }); }); + + it('should print readable information when throw a not error object', (done) => { + let promiseError: Error = null; + let zone: Zone = null; + let task: Task = null; + let rejectObj: TestRejection; + queueZone + .fork({ + name: 'promise-error', + onHandleError: (delegate: ZoneDelegate, current: Zone, target: Zone, error: any): + boolean => { + promiseError = error; + delegate.handleError(target, error); + return false; + } + }) + .run(() => { + zone = Zone.current; + task = Zone.currentTask; + rejectObj = new TestRejection(); + rejectObj.prop1 = 'value1'; + rejectObj.prop2 = 'value2'; + Promise.reject(rejectObj); + expect(promiseError).toBe(null); + }); + setTimeout((): void => null); + setTimeout(() => { + expect(promiseError.message) + .toMatch(/Uncaught \(in promise\):.*: {"prop1":"value1","prop2":"value2"}/); + done(); + }); + }); }); describe('Promise.race', () => {