diff --git a/file-size-limit.json b/file-size-limit.json index c730e24ff..e5e12f2ef 100644 --- a/file-size-limit.json +++ b/file-size-limit.json @@ -3,7 +3,7 @@ { "path": "dist/zone.min.js", "checkTarget": true, - "limit": 42000 + "limit": 42050 } ] } \ No newline at end of file diff --git a/lib/common/promise.ts b/lib/common/promise.ts index d0775f6c1..ce765a260 100644 --- a/lib/common/promise.ts +++ b/lib/common/promise.ts @@ -476,11 +476,6 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr if (NativePromise) { patchThen(NativePromise); - - /*let fetch = global['fetch']; - if (typeof fetch == 'function') { - global['fetch'] = zoneify(fetch); - }*/ } // This is not part of public API, but it is useful for tests, so we expose it. diff --git a/lib/extra/bluebird.ts b/lib/extra/bluebird.ts index 5fd00d3b6..59b4feec4 100644 --- a/lib/extra/bluebird.ts +++ b/lib/extra/bluebird.ts @@ -25,8 +25,14 @@ Zone.__load_patch('bluebird', (global: any, Zone: ZoneType, api: _ZonePrivate) = args[i] = function() { const argSelf: any = this; const argArgs: any = arguments; - zone.scheduleMicroTask('Promise.then', () => { - return func.apply(argSelf, argArgs); + return new Bluebird((res: any, rej: any) => { + zone.scheduleMicroTask('Promise.then', () => { + try { + res(func.apply(argSelf, argArgs)); + } catch (error) { + rej(error); + } + }); }); }; } @@ -35,6 +41,16 @@ Zone.__load_patch('bluebird', (global: any, Zone: ZoneType, api: _ZonePrivate) = }); }); + Bluebird.onPossiblyUnhandledRejection(function(e: any, promise: any) { + try { + Zone.current.runGuarded(() => { + throw e; + }); + } catch (err) { + api.onUnhandledError(err); + } + }); + // override global promise global[api.symbol('ZoneAwarePromise')] = Bluebird; }; diff --git a/test/extra/bluebird.spec.ts b/test/extra/bluebird.spec.ts index 5e2e6eed7..41733e735 100644 --- a/test/extra/bluebird.spec.ts +++ b/test/extra/bluebird.spec.ts @@ -9,6 +9,10 @@ // this spec will not be integrated with Travis CI, because I don't // want to add bluebird into devDependencies, you can run this spec // on your local environment +process.on('unhandledRejection', (reason, p) => { + console.log('Unhandled Rejection at:', p, 'reason:', reason); + // application specific logging, throwing an error, or other logic here +}); describe('bluebird promise', () => { let BluebirdPromise: any; @@ -636,4 +640,156 @@ describe('bluebird promise', () => { }); }); }); + + it('should be able to chain promise', (done: DoneFn) => { + Zone.current.fork({name: 'zone_A'}).run(() => { + new BluebirdPromise((resolve: any, reject: any) => { + expect(Zone.current.name).toEqual('zone_A'); + resolve(1); + }) + .then((r: any) => { + expect(r).toBe(1); + expect(Zone.current.name).toEqual('zone_A'); + return Promise.resolve(2); + }) + .then((r: any) => { + expect(r).toBe(2); + expect(Zone.current.name).toEqual('zone_A'); + }); + }); + Zone.current.fork({name: 'zone_B'}).run(() => { + new BluebirdPromise((resolve: any, reject: any) => { + expect(Zone.current.name).toEqual('zone_B'); + reject(1); + }) + .then( + () => { + fail('should not be here.'); + }, + (r: any) => { + expect(r).toBe(1); + expect(Zone.current.name).toEqual('zone_B'); + return Promise.resolve(2); + }) + .then((r: any) => { + expect(r).toBe(2); + expect(Zone.current.name).toEqual('zone_B'); + done(); + }); + }); + }); + + it('should catch rejected chained bluebird promise', (done: DoneFn) => { + const logs: string[] = []; + const zone = Zone.current.fork({ + name: 'testErrorHandling', + onHandleError: function() { + // should not get here + logs.push('onHandleError'); + return true; + } + }); + + zone.runGuarded(() => { + return BluebirdPromise.resolve() + .then(() => { + throw new Error('test error'); + }) + .catch(() => { + expect(logs).toEqual([]); + done(); + }); + }); + }); + + it('should catch rejected chained global promise', (done: DoneFn) => { + const logs: string[] = []; + const zone = Zone.current.fork({ + name: 'testErrorHandling', + onHandleError: function() { + // should not get here + logs.push('onHandleError'); + return true; + } + }); + + zone.runGuarded(() => { + return Promise.resolve() + .then(() => { + throw new Error('test error'); + }) + .catch(() => { + expect(logs).toEqual([]); + done(); + }); + }); + }); + + it('should catch rejected bluebird promise', (done: DoneFn) => { + const logs: string[] = []; + const zone = Zone.current.fork({ + name: 'testErrorHandling', + onHandleError: function() { + // should not get here + logs.push('onHandleError'); + return true; + } + }); + + zone.runGuarded(() => { + return BluebirdPromise.reject().catch(() => { + expect(logs).toEqual([]); + done(); + }); + }); + }); + + it('should catch rejected global promise', (done: DoneFn) => { + const logs: string[] = []; + const zone = Zone.current.fork({ + name: 'testErrorHandling', + onHandleError: function() { + // should not get here + logs.push('onHandleError'); + return true; + } + }); + + zone.runGuarded(() => { + return Promise.reject(new Error('reject')).catch(() => { + expect(logs).toEqual([]); + done(); + }); + }); + }); + + it('should trigger onHandleError when unhandledRejection', (done: DoneFn) => { + const zone = Zone.current.fork({ + name: 'testErrorHandling', + onHandleError: function() { + setTimeout(done, 100); + return true; + } + }); + + zone.runGuarded(() => { + return Promise.reject(new Error('reject')); + }); + }); + + it('should trigger onHandleError when unhandledRejection in chained Promise', (done: DoneFn) => { + const zone = Zone.current.fork({ + name: 'testErrorHandling', + onHandleError: function() { + setTimeout(done, 100); + return true; + } + }); + + zone.runGuarded(() => { + return Promise.resolve().then(() => { + throw new Error('test'); + }); + }); + }); });