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

Commit a66595a

Browse files
JiaLiPassionmhevery
authored andcommitted
feat(performance): onProperty handler use global wrapFn, other performance improve. (#872)
* feat(performance): onProperty use global wrapFn, other perf improve * add check
1 parent 31d38c1 commit a66595a

14 files changed

Lines changed: 190 additions & 104 deletions

lib/browser/browser.ts

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -91,34 +91,52 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
9191
return pendingTask;
9292
}
9393

94+
const SYMBOL_ADDEVENTLISTENER = zoneSymbol('addEventListener');
95+
const SYMBOL_REMOVEEVENTLISTENER = zoneSymbol('removeEventListener');
96+
97+
let oriAddListener = (XMLHttpRequest.prototype as any)[SYMBOL_ADDEVENTLISTENER];
98+
let oriRemoveListener = (XMLHttpRequest.prototype as any)[SYMBOL_REMOVEEVENTLISTENER];
99+
if (!oriAddListener) {
100+
const XMLHttpRequestEventTarget = window['XMLHttpRequestEventTarget'];
101+
if (XMLHttpRequestEventTarget) {
102+
oriAddListener = XMLHttpRequestEventTarget.prototype[SYMBOL_ADDEVENTLISTENER];
103+
oriRemoveListener = XMLHttpRequestEventTarget.prototype[SYMBOL_REMOVEEVENTLISTENER];
104+
}
105+
}
106+
107+
const READY_STATE_CHANGE = 'readystatechange';
108+
const SCHEDULED = 'scheduled';
109+
94110
function scheduleTask(task: Task) {
95111
(XMLHttpRequest as any)[XHR_SCHEDULED] = false;
96112
const data = <XHROptions>task.data;
113+
const target = data.target;
97114
// remove existing event listener
98-
const listener = data.target[XHR_LISTENER];
99-
const oriAddListener = data.target[zoneSymbol('addEventListener')];
100-
const oriRemoveListener = data.target[zoneSymbol('removeEventListener')];
115+
const listener = target[XHR_LISTENER];
116+
if (!oriAddListener) {
117+
oriAddListener = target[SYMBOL_ADDEVENTLISTENER];
118+
oriRemoveListener = target[SYMBOL_REMOVEEVENTLISTENER];
119+
}
101120

102121
if (listener) {
103-
oriRemoveListener.apply(data.target, ['readystatechange', listener]);
122+
oriRemoveListener.apply(target, [READY_STATE_CHANGE, listener]);
104123
}
105-
const newListener = data.target[XHR_LISTENER] = () => {
106-
if (data.target.readyState === data.target.DONE) {
124+
const newListener = target[XHR_LISTENER] = () => {
125+
if (target.readyState === target.DONE) {
107126
// sometimes on some browsers XMLHttpRequest will fire onreadystatechange with
108127
// readyState=4 multiple times, so we need to check task state here
109-
if (!data.aborted && (XMLHttpRequest as any)[XHR_SCHEDULED] &&
110-
task.state === 'scheduled') {
128+
if (!data.aborted && (XMLHttpRequest as any)[XHR_SCHEDULED] && task.state === SCHEDULED) {
111129
task.invoke();
112130
}
113131
}
114132
};
115-
oriAddListener.apply(data.target, ['readystatechange', newListener]);
133+
oriAddListener.apply(target, [READY_STATE_CHANGE, newListener]);
116134

117-
const storedTask: Task = data.target[XHR_TASK];
135+
const storedTask: Task = target[XHR_TASK];
118136
if (!storedTask) {
119-
data.target[XHR_TASK] = task;
137+
target[XHR_TASK] = task;
120138
}
121-
sendNative.apply(data.target, data.args);
139+
sendNative.apply(target, data.args);
122140
(XMLHttpRequest as any)[XHR_SCHEDULED] = true;
123141
return task;
124142
}
@@ -139,6 +157,7 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
139157
return openNative.apply(self, args);
140158
});
141159

160+
const XMLHTTPREQUEST_SOURCE = 'XMLHttpRequest.send';
142161
const sendNative: Function = patchMethod(
143162
window.XMLHttpRequest.prototype, 'send', () => function(self: any, args: any[]) {
144163
const zone = Zone.current;
@@ -149,15 +168,17 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
149168
const options: XHROptions =
150169
{target: self, isPeriodic: false, delay: null, args: args, aborted: false};
151170
return zone.scheduleMacroTask(
152-
'XMLHttpRequest.send', placeholderCallback, options, scheduleTask, clearTask);
171+
XMLHTTPREQUEST_SOURCE, placeholderCallback, options, scheduleTask, clearTask);
153172
}
154173
});
155174

175+
const STRING_TYPE = 'string';
176+
156177
const abortNative = patchMethod(
157178
window.XMLHttpRequest.prototype, 'abort',
158179
(delegate: Function) => function(self: any, args: any[]) {
159180
const task: Task = findPendingTask(self);
160-
if (task && typeof task.type == 'string') {
181+
if (task && typeof task.type == STRING_TYPE) {
161182
// If the XHR has already completed, do nothing.
162183
// If the XHR has already been aborted, do nothing.
163184
// Fix #569, call abort multiple times before done will cause
@@ -207,7 +228,6 @@ Zone.__load_patch('PromiseRejectionEvent', (global: any, Zone: ZoneType, api: _Z
207228
}
208229
});
209230

210-
211231
Zone.__load_patch('util', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
212232
api.patchOnProperties = patchOnProperties;
213233
api.patchMethod = patchMethod;

lib/browser/define-property.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,17 @@ const _getOwnPropertyDescriptor = (Object as any)[zoneSymbol('getOwnPropertyDesc
1717
Object.getOwnPropertyDescriptor;
1818
const _create = Object.create;
1919
const unconfigurablesKey = zoneSymbol('unconfigurables');
20+
const PROTOTYPE = 'prototype';
21+
const OBJECT = 'object';
22+
const UNDEFINED = 'undefined';
2023

2124
export function propertyPatch() {
2225
Object.defineProperty = function(obj, prop, desc) {
2326
if (isUnconfigurable(obj, prop)) {
2427
throw new TypeError('Cannot assign to read only property \'' + prop + '\' of ' + obj);
2528
}
2629
const originalConfigurableFlag = desc.configurable;
27-
if (prop !== 'prototype') {
30+
if (prop !== PROTOTYPE) {
2831
desc = rewriteDescriptor(obj, prop, desc);
2932
}
3033
return _tryDefineProperty(obj, prop, desc, originalConfigurableFlag);
@@ -38,7 +41,7 @@ export function propertyPatch() {
3841
};
3942

4043
Object.create = <any>function(obj: any, proto: any) {
41-
if (typeof proto === 'object' && !Object.isFrozen(proto)) {
44+
if (typeof proto === OBJECT && !Object.isFrozen(proto)) {
4245
Object.keys(proto).forEach(function(prop) {
4346
proto[prop] = rewriteDescriptor(obj, prop, proto[prop]);
4447
});
@@ -83,7 +86,7 @@ function _tryDefineProperty(obj: any, prop: string, desc: any, originalConfigura
8386
if (desc.configurable) {
8487
// In case of errors, when the configurable flag was likely set by rewriteDescriptor(), let's
8588
// retry with the original flag value
86-
if (typeof originalConfigurableFlag == 'undefined') {
89+
if (typeof originalConfigurableFlag == UNDEFINED) {
8790
delete desc.configurable;
8891
} else {
8992
desc.configurable = originalConfigurableFlag;

lib/browser/property-descriptor.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ const webglEventNames = ['webglcontextrestored', 'webglcontextlost', 'webglconte
215215
const formEventNames = ['autocomplete', 'autocompleteerror'];
216216
const detailEventNames = ['toggle'];
217217
const frameEventNames = ['load'];
218-
const frameSetEventNames = ['blur', 'error', 'focus', 'load', 'resize', 'scroll'];
218+
const frameSetEventNames = ['blur', 'error', 'focus', 'load', 'resize', 'scroll', 'messageerror'];
219219
const marqueeEventNames = ['bounce', 'finish', 'start'];
220220

221221
const XMLHttpRequestEventNames = [
@@ -241,7 +241,7 @@ export function propertyDescriptorPatch(api: _ZonePrivate, _global: any) {
241241
if (isBrowser) {
242242
// in IE/Edge, onProp not exist in window object, but in WindowPrototype
243243
// so we need to pass WindowPrototype to check onProp exist or not
244-
patchOnProperties(window, eventNames, Object.getPrototypeOf(window));
244+
patchOnProperties(window, eventNames.concat(['messageerror']), Object.getPrototypeOf(window));
245245
patchOnProperties(Document.prototype, eventNames);
246246

247247
if (typeof(<any>window)['SVGElement'] !== 'undefined') {
@@ -319,20 +319,21 @@ function canPatchViaPropertyDescriptor() {
319319
Object.defineProperty(XMLHttpRequest.prototype, 'onreadystatechange', xhrDesc || {});
320320
return result;
321321
} else {
322+
const SYMBOL_FAKE_ONREADYSTATECHANGE = zoneSymbol('fakeonreadystatechange');
322323
Object.defineProperty(XMLHttpRequest.prototype, 'onreadystatechange', {
323324
enumerable: true,
324325
configurable: true,
325326
get: function() {
326-
return this[zoneSymbol('fakeonreadystatechange')];
327+
return this[SYMBOL_FAKE_ONREADYSTATECHANGE];
327328
},
328329
set: function(value) {
329-
this[zoneSymbol('fakeonreadystatechange')] = value;
330+
this[SYMBOL_FAKE_ONREADYSTATECHANGE] = value;
330331
}
331332
});
332333
const req = new XMLHttpRequest();
333334
const detectFunc = () => {};
334335
req.onreadystatechange = detectFunc;
335-
const result = (req as any)[zoneSymbol('fakeonreadystatechange')] === detectFunc;
336+
const result = (req as any)[SYMBOL_FAKE_ONREADYSTATECHANGE] === detectFunc;
336337
req.onreadystatechange = null;
337338
return result;
338339
}

lib/browser/websocket.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,15 @@ export function apply(api: _ZonePrivate, _global: any) {
3333
proxySocketProto = socket;
3434
['addEventListener', 'removeEventListener', 'send', 'close'].forEach(function(propName) {
3535
proxySocket[propName] = function() {
36-
return socket[propName].apply(socket, arguments);
36+
const args = Array.prototype.slice.call(arguments);
37+
if (propName === 'addEventListener' || propName === 'removeEventListener') {
38+
const eventName = args.length > 0 ? args[0] : undefined;
39+
if (eventName) {
40+
const propertySymbol = Zone.__symbol__('ON_PROPERTY' + eventName);
41+
socket[propertySymbol] = proxySocket[propertySymbol];
42+
}
43+
}
44+
return socket[propName].apply(socket, args);
3745
};
3846
});
3947
} else {
@@ -42,10 +50,11 @@ export function apply(api: _ZonePrivate, _global: any) {
4250
}
4351

4452
patchOnProperties(proxySocket, ['close', 'error', 'message', 'open'], proxySocketProto);
45-
4653
return proxySocket;
4754
};
55+
56+
const globalWebSocket = _global['WebSocket'];
4857
for (const prop in WS) {
49-
_global['WebSocket'][prop] = WS[prop];
58+
globalWebSocket[prop] = WS[prop];
5059
}
5160
}

lib/common/error-rewrite.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,10 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
104104
// We got called with a `new` operator AND we are subclass of ZoneAwareError
105105
// in that case we have to copy all of our properties to `this`.
106106
Object.keys(error).concat('stack', 'message').forEach((key) => {
107-
if ((error as any)[key] !== undefined) {
107+
const value = (error as any)[key];
108+
if (value !== undefined) {
108109
try {
109-
this[key] = (error as any)[key];
110+
this[key] = value;
110111
} catch (e) {
111112
// ignore the assignment in case it is a setter and it throws.
112113
}
@@ -166,6 +167,7 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
166167
});
167168
}
168169

170+
const ZONE_CAPTURESTACKTRACE = 'zoneCaptureStackTrace';
169171
Object.defineProperty(ZoneAwareError, 'prepareStackTrace', {
170172
get: function() {
171173
return NativeError.prepareStackTrace;
@@ -181,7 +183,7 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
181183
for (let i = 0; i < structuredStackTrace.length; i++) {
182184
const st = structuredStackTrace[i];
183185
// remove the first function which name is zoneCaptureStackTrace
184-
if (st.getFunctionName() === 'zoneCaptureStackTrace') {
186+
if (st.getFunctionName() === ZONE_CAPTURESTACKTRACE) {
185187
structuredStackTrace.splice(i, 1);
186188
break;
187189
}
@@ -196,6 +198,15 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
196198
// run/runGuarded/runTask frames. This is done by creating a detect zone and then threading
197199
// the execution through all of the above methods so that we can look at the stack trace and
198200
// find the frames of interest.
201+
const ZONE_AWARE_ERROR = 'ZoneAwareError';
202+
const ERROR_DOT = 'Error.';
203+
const EMPTY = '';
204+
const RUN_GUARDED = 'runGuarded';
205+
const RUN_TASK = 'runTask';
206+
const RUN = 'run';
207+
const BRACKETS = '(';
208+
const AT = '@';
209+
199210
let detectZone: Zone = Zone.current.fork({
200211
name: 'detect',
201212
onHandleError: function(parentZD: ZoneDelegate, current: Zone, target: Zone, error: any):
@@ -215,18 +226,18 @@ Zone.__load_patch('Error', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
215226
// Chrome: at Zone.run (http://localhost:9876/base/build/lib/zone.js:100:24)
216227
// FireFox: Zone.prototype.run@http://localhost:9876/base/build/lib/zone.js:101:24
217228
// Safari: run@http://localhost:9876/base/build/lib/zone.js:101:24
218-
let fnName: string = frame.split('(')[0].split('@')[0];
229+
let fnName: string = frame.split(BRACKETS)[0].split(AT)[0];
219230
let frameType = FrameType.transition;
220-
if (fnName.indexOf('ZoneAwareError') !== -1) {
231+
if (fnName.indexOf(ZONE_AWARE_ERROR) !== -1) {
221232
zoneAwareFrame1 = frame;
222-
zoneAwareFrame2 = frame.replace('Error.', '');
233+
zoneAwareFrame2 = frame.replace(ERROR_DOT, EMPTY);
223234
blackListedStackFrames[zoneAwareFrame2] = FrameType.blackList;
224235
}
225-
if (fnName.indexOf('runGuarded') !== -1) {
236+
if (fnName.indexOf(RUN_GUARDED) !== -1) {
226237
runGuardedFrame = true;
227-
} else if (fnName.indexOf('runTask') !== -1) {
238+
} else if (fnName.indexOf(RUN_TASK) !== -1) {
228239
runTaskFrame = true;
229-
} else if (fnName.indexOf('run') !== -1) {
240+
} else if (fnName.indexOf(RUN) !== -1) {
230241
runFrame = true;
231242
} else {
232243
frameType = FrameType.blackList;

lib/common/promise.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
4848
}
4949
};
5050

51+
const UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL = __symbol__('unhandledPromiseRejectionHandler');
52+
5153
function handleUnhandledRejection(e: any) {
5254
api.onUnhandledError(e);
5355
try {
54-
const handler = (Zone as any)[__symbol__('unhandledPromiseRejectionHandler')];
56+
const handler = (Zone as any)[UNHANDLED_PROMISE_REJECTION_HANDLER_SYMBOL];
5557
if (handler && typeof handler === 'function') {
5658
handler.apply(this, [e]);
5759
}
@@ -104,18 +106,23 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
104106
};
105107
};
106108

109+
const TYPE_ERROR = 'Promise resolved with itself';
110+
const OBJECT = 'object';
111+
const FUNCTION = 'function';
112+
const CURRENT_TASK_SYMBOL = __symbol__('currentTask');
113+
107114
// Promise Resolution
108115
function resolvePromise(
109116
promise: ZoneAwarePromise<any>, state: boolean, value: any): ZoneAwarePromise<any> {
110117
const onceWrapper = once();
111118
if (promise === value) {
112-
throw new TypeError('Promise resolved with itself');
119+
throw new TypeError(TYPE_ERROR);
113120
}
114121
if ((promise as any)[symbolState] === UNRESOLVED) {
115122
// should only get value.then once based on promise spec.
116123
let then: any = null;
117124
try {
118-
if (typeof value === 'object' || typeof value === 'function') {
125+
if (typeof value === OBJECT || typeof value === FUNCTION) {
119126
then = value && value.then;
120127
}
121128
} catch (err) {
@@ -130,7 +137,7 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
130137
(value as any)[symbolState] !== UNRESOLVED) {
131138
clearRejectedNoCatch(<Promise<any>>value);
132139
resolvePromise(promise, (value as any)[symbolState], (value as any)[symbolValue]);
133-
} else if (state !== REJECTED && typeof then === 'function') {
140+
} else if (state !== REJECTED && typeof then === FUNCTION) {
134141
try {
135142
then.apply(value, [
136143
onceWrapper(makeResolver(promise, state)), onceWrapper(makeResolver(promise, false))
@@ -148,7 +155,7 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
148155
// record task information in value when error occurs, so we can
149156
// do some additional work such as render longStackTrace
150157
if (state === REJECTED && value instanceof Error) {
151-
(value as any)[__symbol__('currentTask')] = Zone.currentTask;
158+
(value as any)[CURRENT_TASK_SYMBOL] = Zone.currentTask;
152159
}
153160

154161
for (let i = 0; i < queue.length;) {
@@ -176,6 +183,7 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
176183
return promise;
177184
}
178185

186+
const REJECTION_HANDLED_HANDLER = __symbol__('rejectionHandledHandler');
179187
function clearRejectedNoCatch(promise: ZoneAwarePromise<any>): void {
180188
if ((promise as any)[symbolState] === REJECTED_NO_CATCH) {
181189
// if the promise is rejected no catch status
@@ -184,8 +192,8 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
184192
// windows.rejectionhandled eventHandler or nodejs rejectionHandled
185193
// eventHandler
186194
try {
187-
const handler = (Zone as any)[__symbol__('rejectionHandledHandler')];
188-
if (handler && typeof handler === 'function') {
195+
const handler = (Zone as any)[REJECTION_HANDLED_HANDLER];
196+
if (handler && typeof handler === FUNCTION) {
189197
handler.apply(this, [{rejection: (promise as any)[symbolValue], promise: promise}]);
190198
}
191199
} catch (err) {
@@ -204,8 +212,8 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
204212
onFulfilled?: (value: R) => U, onRejected?: (error: any) => U): void {
205213
clearRejectedNoCatch(promise);
206214
const delegate = (promise as any)[symbolState] ?
207-
(typeof onFulfilled === 'function') ? onFulfilled : forwardResolution :
208-
(typeof onRejected === 'function') ? onRejected : forwardRejection;
215+
(typeof onFulfilled === FUNCTION) ? onFulfilled : forwardResolution :
216+
(typeof onRejected === FUNCTION) ? onRejected : forwardRejection;
209217
zone.scheduleMicroTask(source, () => {
210218
try {
211219
resolvePromise(
@@ -216,9 +224,11 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
216224
});
217225
}
218226

227+
const ZONE_AWARE_PROMISE_TO_STRING = 'function ZoneAwarePromise() { [native code] }';
228+
219229
class ZoneAwarePromise<R> implements Promise<R> {
220230
static toString() {
221-
return 'function ZoneAwarePromise() { [native code] }';
231+
return ZONE_AWARE_PROMISE_TO_STRING;
222232
}
223233

224234
static resolve<R>(value: R): Promise<R> {
@@ -364,7 +374,7 @@ Zone.__load_patch('ZoneAwarePromise', (global: any, Zone: ZoneType, api: _ZonePr
364374
patchThen(NativePromise);
365375

366376
let fetch = global['fetch'];
367-
if (typeof fetch == 'function') {
377+
if (typeof fetch == FUNCTION) {
368378
global['fetch'] = zoneify(fetch);
369379
}
370380
}

0 commit comments

Comments
 (0)