Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
fix(promise): can set native promise after loading zone.js (#899)
Browse files Browse the repository at this point in the history
* fix(promise): can set native promise after loading zone.js

* disable SL_IOS9
  • Loading branch information
JiaLiPassion authored and mhevery committed Sep 15, 2017
1 parent e999593 commit 956c729
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 23 deletions.
1 change: 1 addition & 0 deletions karma-base.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
2 changes: 1 addition & 1 deletion karma-dist-sauce-jasmine.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@

module.exports = function (config) {
require('./karma-dist-jasmine.conf.js')(config);
require('./sauce.conf')(config);
require('./sauce.conf')(config, ['SL_IOS9']);
};
38 changes: 38 additions & 0 deletions lib/common/promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
4 changes: 4 additions & 0 deletions lib/zone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) =>
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 13 additions & 2 deletions sauce.conf.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Sauce configuration

module.exports = function (config) {
module.exports = function (config, ignoredLaunchers) {
// The WS server is not available with Sauce
config.files.unshift('test/saucelabs.js');

var customLaunchers = {
var basicLaunchers = {
'SL_CHROME': {
base: 'SauceLabs',
browserName: 'chrome',
Expand Down Expand Up @@ -153,6 +153,17 @@ module.exports = function (config) {
}
};

var customLaunchers = {};
if (!ignoredLaunchers) {
customLaunchers = basicLaunchers;
} else {
Object.keys(basicLaunchers).forEach(function(key) {
if (ignoredLaunchers.filter(function(ignore) {return ignore === key;}).length === 0) {
customLaunchers[key] = basicLaunchers[key];
}
});
}

config.set({
captureTimeout: 120000,
browserNoActivityTimeout: 240000,
Expand Down
27 changes: 26 additions & 1 deletion test/common/Promise.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
});
Expand Down
15 changes: 11 additions & 4 deletions test/common/zone.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
});
Expand Down
7 changes: 5 additions & 2 deletions test/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -43,4 +46,4 @@ browserPatchedPromise.then(() => {
console.error(error.stack || error);
});
});
});
});
20 changes: 7 additions & 13 deletions test/rxjs/rxjs.Observable.combine.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,11 @@ describe('Observable.combine', () => {
const constructorZone1: Zone = Zone.current.fork({name: 'Constructor Zone1'});
const subscriptionZone: Zone = Zone.current.fork({name: 'Subscription Zone'});
observable1 = constructorZone1.run(() => {
const source = Rx.Observable.interval(10);
const highOrder = source
.map((src: any) => {
expect(Zone.current.name).toEqual(constructorZone1.name);
return Rx.Observable.interval(50).take(3);
})
.take(2);
const source = Rx.Observable.of(1, 2, 3);
const highOrder = source.map((src: any) => {
expect(Zone.current.name).toEqual(constructorZone1.name);
return Rx.Observable.of(src);
});
return highOrder.combineAll((x: any, y: any) => {
expect(Zone.current.name).toEqual(constructorZone1.name);
return {x: x, y: y};
Expand All @@ -76,14 +74,10 @@ describe('Observable.combine', () => {
() => {
log.push('completed');
expect(Zone.current.name).toEqual(subscriptionZone.name);
expect(log).toEqual([
{x: 0, y: 0}, {x: 1, y: 0}, {x: 1, y: 1}, {x: 2, y: 1}, {x: 2, y: 2}, 'completed'
]);
expect(log).toEqual([{x: 1, y: 2}, 'completed']);
done();
});
});

expect(log).toEqual([]);
}, Zone.root));

it('combineLatest func callback should run in the correct zone', () => {
Expand Down Expand Up @@ -141,4 +135,4 @@ describe('Observable.combine', () => {

expect(log).toEqual([7, 8, 9, 'completed']);
});
});
});

0 comments on commit 956c729

Please sign in to comment.