diff --git a/packages/zone.js/lib/common/promise.ts b/packages/zone.js/lib/common/promise.ts index 7eca3b299580..dda37fd94218 100644 --- a/packages/zone.js/lib/common/promise.ts +++ b/packages/zone.js/lib/common/promise.ts @@ -34,7 +34,7 @@ export function patchPromise(Zone: ZoneType): void { api.onUnhandledError = (e: any) => { if (api.showUncaughtError()) { const rejection = e && e.rejection; - if (rejection) { + if (rejection && e.zone && e.task) { console.error( 'Unhandled Promise rejection:', rejection instanceof Error ? rejection.message : rejection, diff --git a/packages/zone.js/lib/zone-impl.ts b/packages/zone.js/lib/zone-impl.ts index 4465bbaadeb9..1cc8c6433f76 100644 --- a/packages/zone.js/lib/zone-impl.ts +++ b/packages/zone.js/lib/zone-impl.ts @@ -1435,10 +1435,13 @@ export function initZone(): ZoneType { task.runCount++; return task.zone.runTask(task, target, args); } finally { - if (_numberOfNestedTaskFrames === 1 && !global[enableNativeMicrotaskDraining]) { - drainMicroTaskQueueSynchronously(); + try { + if (_numberOfNestedTaskFrames === 1 && !global[enableNativeMicrotaskDraining]) { + drainMicroTaskQueueSynchronously(); + } + } finally { + _numberOfNestedTaskFrames--; } - _numberOfNestedTaskFrames--; } } @@ -1551,27 +1554,32 @@ export function initZone(): ZoneType { _isDrainingMicrotaskQueue = true; - while (_microTaskQueue.length) { - const queue = _microTaskQueue; - _microTaskQueue = []; + try { + while (_microTaskQueue.length) { + const queue = _microTaskQueue; + _microTaskQueue = []; - for (const task of queue) { + for (const task of queue) { + try { + task.zone.runTask(task, null, null); + } catch (error) { + _api.onUnhandledError(error as Error); + } + } + } + } finally { + // The order matters! + if (global[enableNativeMicrotaskDraining]) { + _isDrainingMicrotaskQueue = false; + _api.microtaskDrainDone(); + } else { try { - task.zone.runTask(task, null, null); - } catch (error) { - _api.onUnhandledError(error as Error); + _api.microtaskDrainDone(); + } finally { + _isDrainingMicrotaskQueue = false; } } } - - // The order matters! - if (global[enableNativeMicrotaskDraining]) { - _isDrainingMicrotaskQueue = false; - _api.microtaskDrainDone(); - } else { - _api.microtaskDrainDone(); - _isDrainingMicrotaskQueue = false; - } } ////////////////////////////////////////////////////// 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 24ba5592d4c8..7388c89fb66a 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 @@ -110,4 +110,28 @@ describe('disable wrap uncaught promise rejection', () => { done(); }); }); + + it('should handle a custom object rejection with a rejection property without crashing the error logger', async () => { + await jasmine.spyOnGlobalErrorsAsync(() => { + const originalConsoleError = console.error; + console.error = jasmine.createSpy('consoleErr'); + + const rejectObj = { + rejection: 'custom-inner-rejection', + message: 'custom-error-message', + }; + + Zone.current.fork({name: 'promise-error-zone'}).run(() => { + Promise.reject(rejectObj); + }); + + return new Promise((res) => { + setTimeout(() => { + expect(console.error).toHaveBeenCalledWith(rejectObj); + console.error = originalConsoleError; + res(); + }); + }); + }); + }); });