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

Commit 8f7f096

Browse files
committed
feat(core): add runOutsideOfZone ability
1 parent 31fc127 commit 8f7f096

8 files changed

+78
-23
lines changed

lib/browser/browser.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
230230
task.invoke();
231231
}
232232
}
233-
});
233+
}, true);
234234

235235
const abortNative =
236236
patchMethod(XMLHttpRequestPrototype, 'abort', () => function(self: any, args: any[]) {
@@ -244,13 +244,12 @@ Zone.__load_patch('XHR', (global: any, Zone: ZoneType) => {
244244
return;
245245
}
246246
task.zone.cancelTask(task);
247-
} else if ((Zone.current as any)[fetchTaskAborting] === true) {
248-
// the abort is called from fetch polyfill, we need to call native abort of XHR.
249-
return abortNative!.apply(self, args);
247+
return;
250248
}
251249
// Otherwise, we are trying to abort an XHR which has not yet been sent, so there is no
252-
// task
253-
// to cancel. Do nothing.
250+
// task to cancel. But we need to use abortNative to abort the XHR in case the send
251+
// is called outside of Zone.
252+
return abortNative!.apply(self, args);
254253
});
255254
}
256255
});

lib/browser/webapis-media-query.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Zone.__load_patch('mediaQuery', (global: any, Zone: ZoneType, api: _ZonePrivate)
1616
} else {
1717
return delegate.apply(self, args);
1818
}
19-
});
19+
}, true);
2020
}
2121

2222
function patchRemoveListener(proto: any) {

lib/browser/webapis-resize-observer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Zone.__load_patch('ResizeObserver', (global: any, Zone: any, api: _ZonePrivate)
5858
targets.push(target);
5959
target[resizeObserverSymbol] = Zone.current;
6060
return delegate.apply(self, args);
61-
});
61+
}, true);
6262

6363
api.patchMethod(
6464
ResizeObserver.prototype, 'unobserve', (delegate: Function) => (self: any, args: any[]) => {

lib/common/events.ts

+37-7
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,9 @@ export function patchEventTarget(
331331
return function() {
332332
const target = this || _global;
333333
let delegate = arguments[1];
334-
if (!delegate) {
334+
// if handler is not available or we are in root zone
335+
// use nativeListener
336+
if (!delegate || Zone.current === Zone.root) {
335337
return nativeListener.apply(this, arguments);
336338
}
337339

@@ -488,6 +490,10 @@ export function patchEventTarget(
488490
}
489491

490492
proto[REMOVE_EVENT_LISTENER] = function() {
493+
const delegate = arguments[1];
494+
if (!delegate) {
495+
return nativeRemoveEventListener.apply(this, arguments);
496+
}
491497
const target = this || _global;
492498
const eventName = arguments[0];
493499
const options = arguments[2];
@@ -503,11 +509,6 @@ export function patchEventTarget(
503509
capture = options ? !!options.capture : false;
504510
}
505511

506-
const delegate = arguments[1];
507-
if (!delegate) {
508-
return nativeRemoveEventListener.apply(this, arguments);
509-
}
510-
511512
if (validateHandler &&
512513
!validateHandler(nativeRemoveEventListener, delegate, target, arguments)) {
513514
return;
@@ -553,11 +554,32 @@ export function patchEventTarget(
553554

554555
const listeners: any[] = [];
555556
const tasks = findEventTasks(target, eventName);
557+
const invokes: any[]|null = nativeListeners ? [] : null;
556558

557559
for (let i = 0; i < tasks.length; i++) {
558560
const task: any = tasks[i];
559561
let delegate = task.originalDelegate ? task.originalDelegate : task.callback;
560562
listeners.push(delegate);
563+
if (invokes) {
564+
invokes.push(task.invoke);
565+
}
566+
}
567+
if (nativeListeners) {
568+
const natives = nativeListeners.apply(this, arguments);
569+
if (natives && invokes) {
570+
natives.forEach((n: any) => {
571+
let found = false;
572+
for (let i = 0; i < invokes.length; i++) {
573+
if (invokes[i] === n) {
574+
found = true;
575+
break;
576+
}
577+
}
578+
if (!found) {
579+
listeners.push(n);
580+
}
581+
});
582+
}
561583
}
562584
return listeners;
563585
};
@@ -580,8 +602,12 @@ export function patchEventTarget(
580602
this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, evtName);
581603
}
582604
}
583-
// remove removeListener listener finally
605+
// call native again in case some event handler was not
606+
// use patched version of addListener
584607
this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, 'removeListener');
608+
if (nativeRemoveAllListeners) {
609+
nativeRemoveAllListeners.call(this);
610+
}
585611
} else {
586612
const symbolEventNames = zoneSymbolEventNames[eventName];
587613
if (symbolEventNames) {
@@ -609,6 +635,10 @@ export function patchEventTarget(
609635
}
610636
}
611637
}
638+
639+
if (nativeRemoveAllListeners) {
640+
nativeRemoveAllListeners.call(this, eventName);
641+
}
612642
}
613643

614644
if (returnTarget) {

lib/common/fetch.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ Zone.__load_patch('fetch', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
3636
}
3737
const placeholder = function() {};
3838
global['fetch'] = function() {
39+
// if in root zone, just use native fetch directly.
40+
if (Zone.current === Zone.root) {
41+
return fetch.apply(this, arguments);
42+
}
3943
const args = Array.prototype.slice.call(arguments);
4044
const options = args.length > 1 ? args[1] : null;
4145
const signal = options && options.signal;
@@ -97,4 +101,4 @@ Zone.__load_patch('fetch', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
97101
}
98102
});
99103
};
100-
});
104+
});

lib/common/timers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export function patchTimer(window: any, setName: string, cancelName: string, nam
9999
// cause an error by calling it directly.
100100
return delegate.apply(window, args);
101101
}
102-
});
102+
}, true);
103103

104104
clearNative =
105105
patchMethod(window, cancelName, (delegate: Function) => function(self: any, args: any[]) {

lib/common/utils.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,8 @@ export function setShouldCopySymbolProperties(flag: boolean) {
389389
export function patchMethod(
390390
target: any, name: string,
391391
patchFn: (delegate: Function, delegateName: string, name: string) => (self: any, args: any[]) =>
392-
any): Function|null {
392+
any,
393+
checkInZone = false): Function|null {
393394
let proto = target;
394395
while (proto && !proto.hasOwnProperty(name)) {
395396
proto = ObjectGetPrototypeOf(proto);
@@ -409,6 +410,10 @@ export function patchMethod(
409410
if (isPropertyWritable(desc)) {
410411
const patchDelegate = patchFn(delegate!, delegateName, name);
411412
proto[name] = function() {
413+
// if we are in root zone, just use native delegate.
414+
if (checkInZone && Zone.current === Zone.root && delegate) {
415+
return delegate.apply(this, arguments);
416+
}
412417
return patchDelegate(this, arguments as any);
413418
};
414419
attachOriginToPatched(proto[name], delegate);
@@ -449,7 +454,7 @@ export function patchMacroTask(
449454
// cause an error by calling it directly.
450455
return delegate.apply(self, args);
451456
}
452-
});
457+
}, true);
453458
}
454459

455460
export interface MicroTaskMeta extends TaskData {
@@ -480,7 +485,7 @@ export function patchMicroTask(
480485
// cause an error by calling it directly.
481486
return delegate.apply(self, args);
482487
}
483-
});
488+
}, true);
484489
}
485490

486491
export function attachOriginToPatched(patched: Function, original: any) {

lib/zone.ts

+20-3
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,8 @@ interface _ZonePrivate {
327327
patchMethod:
328328
(target: any, name: string,
329329
patchFn: (delegate: Function, delegateName: string, name: string) =>
330-
(self: any, args: any[]) => any) => Function | null;
330+
(self: any, args: any[]) => any,
331+
checkInZone?: boolean) => Function | null;
331332
bindArguments: (args: any[], source: string) => any[];
332333
}
333334

@@ -710,7 +711,6 @@ const Zone: ZoneType = (function(global: any) {
710711
return this._name;
711712
}
712713

713-
714714
private _parent: Zone|null;
715715
private _name: string;
716716
private _properties: {[key: string]: any};
@@ -752,6 +752,9 @@ const Zone: ZoneType = (function(global: any) {
752752
const _callback = this._zoneDelegate.intercept(this, callback, source);
753753
const zone: Zone = this;
754754
return function() {
755+
if (zone === rootZone) {
756+
return _callback.apply(this, arguments);
757+
}
755758
return zone.runGuarded(_callback, (this as any), <any>arguments, source);
756759
} as any as T;
757760
}
@@ -767,6 +770,18 @@ const Zone: ZoneType = (function(global: any) {
767770
}
768771
}
769772

773+
public runOutsideOfZone(callback: Function, applyThis?: any, applyArgs?: any[]): any;
774+
public runOutsideOfZone<T>(callback: (...args: any[]) => T, applyThis?: any, applyArgs?: any[]):
775+
T {
776+
const _originalZoneFrame = _currentZoneFrame;
777+
_currentZoneFrame = rootZoneFrame;
778+
try {
779+
return callback.apply(applyThis, applyArgs);
780+
} finally {
781+
_currentZoneFrame = _originalZoneFrame;
782+
}
783+
}
784+
770785
public runGuarded(callback: Function, applyThis?: any, applyArgs?: any[], source?: string): any;
771786
public runGuarded<T>(
772787
callback: (...args: any[]) => T, applyThis: any = null, applyArgs?: any[],
@@ -1352,7 +1367,9 @@ const Zone: ZoneType = (function(global: any) {
13521367
}
13531368
},
13541369
};
1355-
let _currentZoneFrame: _ZoneFrame = {parent: null, zone: new Zone(null, null)};
1370+
const rootZone = new Zone(null, null);
1371+
const rootZoneFrame: _ZoneFrame = {parent: null, zone: rootZone};
1372+
let _currentZoneFrame: _ZoneFrame = rootZoneFrame;
13561373
let _currentTask: Task|null = null;
13571374
let _numberOfNestedTaskFrames = 0;
13581375

0 commit comments

Comments
 (0)