From c7542a1d09b27b027a0367a1f3732a908cdd617a Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Sun, 21 Jul 2019 19:49:41 +0900 Subject: [PATCH] fix(zone.js): don't fire unhandledrejection if Zone handled error (#31718) Close #31701 PR Close #31718 --- packages/zone.js/lib/extra/bluebird.ts | 32 +++++++++++- packages/zone.js/test/extra/bluebird.spec.ts | 53 ++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/packages/zone.js/lib/extra/bluebird.ts b/packages/zone.js/lib/extra/bluebird.ts index 92d74f920a..c97ecbfe8f 100644 --- a/packages/zone.js/lib/extra/bluebird.ts +++ b/packages/zone.js/lib/extra/bluebird.ts @@ -41,10 +41,40 @@ Zone.__load_patch('bluebird', (global: any, Zone: ZoneType, api: _ZonePrivate) = }); }); + if (typeof window !== 'undefined') { + window.addEventListener('unhandledrejection', function(event: any) { + const error = event.detail && event.detail.reason; + if (error && error.isHandledByZone) { + event.preventDefault(); + if (typeof event.stopImmediatePropagation === 'function') { + event.stopImmediatePropagation(); + } + } + }); + } else if (typeof process !== 'undefined') { + process.on('unhandledRejection', (reason: any, p: any) => { + if (reason && reason.isHandledByZone) { + const listeners = process.listeners('unhandledRejection'); + if (listeners) { + // remove unhandledRejection listeners so the callback + // will not be triggered. + process.removeAllListeners('unhandledRejection'); + process.nextTick(() => { + listeners.forEach(listener => process.on('unhandledRejection', listener)); + }); + } + } + }); + } + Bluebird.onPossiblyUnhandledRejection(function(e: any, promise: any) { try { - Zone.current.runGuarded(() => { throw e; }); + Zone.current.runGuarded(() => { + e.isHandledByZone = true; + throw e; + }); } catch (err) { + err.isHandledByZone = false; api.onUnhandledError(err); } }); diff --git a/packages/zone.js/test/extra/bluebird.spec.ts b/packages/zone.js/test/extra/bluebird.spec.ts index 1f935e4067..8e8e5993ff 100644 --- a/packages/zone.js/test/extra/bluebird.spec.ts +++ b/packages/zone.js/test/extra/bluebird.spec.ts @@ -700,4 +700,57 @@ describe('bluebird promise', () => { zone.runGuarded(() => { return Promise.resolve().then(() => { throw new Error('test'); }); }); }); + + it('should not trigger unhandledrejection if zone.onHandleError return false', (done: DoneFn) => { + const listener = function() { fail('should not be here'); }; + + if (typeof window !== 'undefined') { + window.addEventListener('unhandledrejection', listener); + } else if (typeof process !== 'undefined') { + process.on('unhandledRejection', listener); + } + + const zone = Zone.current.fork({ + name: 'testErrorHandling', + onHandleError: function() { + setTimeout(() => { + if (typeof window !== 'undefined') { + window.removeEventListener('unhandledrejection', listener); + } else if (typeof process !== 'undefined') { + process.removeListener('unhandledRejection', listener); + } + done(); + }, 500); + return false; + } + }); + + zone.runGuarded(() => { return Promise.resolve().then(() => { throw new Error('test'); }); }); + }); + + it('should trigger unhandledrejection if zone.onHandleError return true', (done: DoneFn) => { + const listener = function(event: any) { + if (typeof window !== 'undefined') { + expect(event.detail.reason.message).toEqual('test'); + } else if (typeof process !== 'undefined') { + expect(event.message).toEqual('test'); + } + if (typeof window !== 'undefined') { + window.removeEventListener('unhandledrejection', listener); + } else if (typeof process !== 'undefined') { + process.removeListener('unhandledRejection', listener); + } + done(); + }; + if (typeof window !== 'undefined') { + window.addEventListener('unhandledrejection', listener); + } else if (typeof process !== 'undefined') { + process.on('unhandledRejection', listener); + } + + const zone = + Zone.current.fork({name: 'testErrorHandling', onHandleError: function() { return true; }}); + + zone.runGuarded(() => { return Promise.resolve().then(() => { throw new Error('test'); }); }); + }); });