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'); }); }); + }); });