From d07a8329999d351fcf6aa4717794e3c4ac1d9068 Mon Sep 17 00:00:00 2001 From: "JiaLi.Passion" Date: Tue, 12 Sep 2017 13:47:17 +0900 Subject: [PATCH] fix(promise): can set native promise after loading zone.js --- karma-base.conf.js | 1 + lib/common/promise.ts | 38 +++++++++++++++++++++++++++++++++++++ lib/zone.ts | 4 ++++ test/common/Promise.spec.ts | 27 +++++++++++++++++++++++++- test/common/zone.spec.ts | 15 +++++++++++---- test/main.ts | 7 +++++-- 6 files changed, 85 insertions(+), 7 deletions(-) diff --git a/karma-base.conf.js b/karma-base.conf.js index b733dbe4f..26ffa771a 100644 --- a/karma-base.conf.js +++ b/karma-base.conf.js @@ -16,6 +16,7 @@ module.exports = function (config) { {pattern: 'node_modules/rxjs/**/**/*.js', included: false, watched: false }, {pattern: 'node_modules/rxjs/**/**/*.js.map', included: false, watched: false }, {pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false }, + {pattern: 'node_modules/es6-promise/**/*.js', included: false, watched: false }, {pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false }, {pattern: 'test/assets/**/*.*', watched: true, served: true, included: false}, {pattern: 'build/**/*.js.map', watched: true, served: true, included: false}, diff --git a/lib/common/promise.ts b/lib/common/promise.ts index 6dcb4e76b..57671f2d8 100644 --- a/lib/common/promise.ts +++ b/lib/common/promise.ts @@ -330,6 +330,44 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr ZoneAwarePromise['all'] = ZoneAwarePromise.all; const NativePromise = global[symbolPromise] = global['Promise']; + const ZONE_AWARE_PROMISE = Zone.__symbol__('ZoneAwarePromise'); + + let desc = Object.getOwnPropertyDescriptor(global, 'Promise'); + if (!desc || desc.configurable) { + desc && delete desc.writable; + desc && delete desc.value; + if (!desc) { + desc = {configurable: true, enumerable: true}; + } + desc.get = function() { + // if we already set ZoneAwarePromise, use patched one + // otherwise return native one. + return global[ZONE_AWARE_PROMISE] ? global[ZONE_AWARE_PROMISE] : global[symbolPromise]; + }; + desc.set = function(NewNativePromise) { + if (NewNativePromise === ZoneAwarePromise) { + // if the NewNativePromise is ZoneAwarePromise + // save to global + global[ZONE_AWARE_PROMISE] = NewNativePromise; + } else { + // if the NewNativePromise is not ZoneAwarePromise + // for example: after load zone.js, some library just + // set es6-promise to global, if we set it to global + // directly, assertZonePatched will fail and angular + // will not loaded, so we just set the NewNativePromise + // to global[symbolPromise], so the result is just like + // we load ES6 Promise before zone.js + global[symbolPromise] = NewNativePromise; + if (!NewNativePromise.prototype[symbolThen]) { + patchThen(NewNativePromise); + } + api.setNativePromise(NewNativePromise); + } + }; + + Object.defineProperty(global, 'Promise', desc); + } + global['Promise'] = ZoneAwarePromise; const symbolThenPatched = __symbol__('thenPatched'); diff --git a/lib/zone.ts b/lib/zone.ts index 2e4288a99..d873bb965 100644 --- a/lib/zone.ts +++ b/lib/zone.ts @@ -322,6 +322,7 @@ interface _ZonePrivate { showUncaughtError: () => boolean; patchEventTarget: (global: any, apis: any[], options?: any) => boolean[]; patchOnProperties: (obj: any, properties: string[]) => void; + setNativePromise: (nativePromise: any) => void; patchMethod: (target: any, name: string, patchFn: (delegate: Function, delegateName: string, name: string) => @@ -1318,6 +1319,9 @@ const Zone: ZoneType = (function(global: any) { patchEventTarget: () => [], patchOnProperties: noop, patchMethod: () => noop, + setNativePromise: (NativePromise: any) => { + nativeMicroTaskQueuePromise = NativePromise.resolve(0); + }, }; let _currentZoneFrame: _ZoneFrame = {parent: null, zone: new Zone(null, null)}; let _currentTask: Task = null; diff --git a/test/common/Promise.spec.ts b/test/common/Promise.spec.ts index f8f0e8e1b..00c4db71f 100644 --- a/test/common/Promise.spec.ts +++ b/test/common/Promise.spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {isNode} from '../../lib/common/utils'; +import {isNode, zoneSymbol} from '../../lib/common/utils'; import {ifEnvSupports} from '../test-util'; declare const global: any; @@ -58,6 +58,31 @@ describe( log = []; }); + xit('should allow set es6 Promise after load ZoneAwarePromise', (done) => { + const ES6Promise = require('es6-promise').Promise; + const NativePromise = global[zoneSymbol('Promise')]; + + try { + global['Promise'] = ES6Promise; + Zone.assertZonePatched(); + expect(global[zoneSymbol('Promise')]).toBe(ES6Promise); + const promise = Promise.resolve(0); + console.log('promise', promise); + promise + .then(value => { + expect(value).toBe(0); + done(); + }) + .catch(error => { + fail(error); + }); + } finally { + global['Promise'] = NativePromise; + Zone.assertZonePatched(); + expect(global[zoneSymbol('Promise')]).toBe(NativePromise); + } + }); + it('should pretend to be a native code', () => { expect(String(Promise).indexOf('[native code]') >= 0).toBe(true); }); diff --git a/test/common/zone.spec.ts b/test/common/zone.spec.ts index 95cd0219a..f969df913 100644 --- a/test/common/zone.spec.ts +++ b/test/common/zone.spec.ts @@ -5,6 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import {zoneSymbol} from '../../lib/common/utils'; describe('Zone', function() { const rootZone = Zone.current; @@ -326,17 +327,23 @@ describe('Zone', function() { Zone.assertZonePatched(); }); - it('should throw when Promise has been patched', () => { - class WrongPromise {} + it('should keep ZoneAwarePromise has been patched', () => { + class WrongPromise { + static resolve(value: any) {} + + then() {} + } const ZoneAwarePromise = global.Promise; + const NativePromise = (global as any)[zoneSymbol('Promise')]; global.Promise = WrongPromise; try { expect(ZoneAwarePromise).toBeTruthy(); - expect(() => Zone.assertZonePatched()).toThrow(); + Zone.assertZonePatched(); + expect(global.Promise).toBe(ZoneAwarePromise); } finally { // restore it. - global.Promise = ZoneAwarePromise; + global.Promise = NativePromise; } Zone.assertZonePatched(); }); diff --git a/test/main.ts b/test/main.ts index 117e274e6..f8c33f3ea 100644 --- a/test/main.ts +++ b/test/main.ts @@ -16,7 +16,10 @@ __karma__.loaded = function() {}; (window as any).global = window; System.config({ defaultJSExtensions: true, - map: {'rxjs': 'base/node_modules/rxjs'}, + map: { + 'rxjs': 'base/node_modules/rxjs', + 'es6-promise': 'base/node_modules/es6-promise/dist/es6-promise' + }, }); let browserPatchedPromise: any = null; @@ -43,4 +46,4 @@ browserPatchedPromise.then(() => { console.error(error.stack || error); }); }); -}); \ No newline at end of file +});