refactor(core): implement __ignore_ng_zone__ flag (#51339)

This flag allows message event listeners to prevent callbacks from executing within the NgZone if they contain a special `__ignore_ng_zone__` flag.

This functionality is built with Angular DevTools in mind, where it prevents an infinite change detection loop in inspecting applications that have message event listeners:

CD -> Inspected app emits componentTreeDirty event to DevTools -> DevTools emits event to get new component Tree from Inspected app -> Inspected app message event listener fires -> CD

PR Close #51339
This commit is contained in:
AleksanderBodurri
2023-08-12 03:12:42 -04:00
committed by Jessica Janiuk
parent 2d96dc27b7
commit 04bffaa41c
+19
View File
@@ -419,6 +419,10 @@ function forkInnerZoneWithAngularBehavior(zone: NgZonePrivate) {
onInvokeTask:
(delegate: ZoneDelegate, current: Zone, target: Zone, task: Task, applyThis: any,
applyArgs: any): any => {
if (shouldBeIgnoredByZone(applyArgs)) {
return delegate.invokeTask(target, task, applyThis, applyArgs);
}
try {
onEnter(zone);
return delegate.invokeTask(target, task, applyThis, applyArgs);
@@ -585,3 +589,18 @@ export function isStableFactory() {
});
return merge(isCurrentlyStable, isStable.pipe(share()));
}
function shouldBeIgnoredByZone(applyArgs: unknown): boolean {
if (!Array.isArray(applyArgs)) {
return false;
}
// We should only ever get 1 arg passed through to invokeTask.
// Short circuit here incase that behavior changes.
if (applyArgs.length !== 1) {
return false;
}
// Prevent triggering change detection when the __ignore_ng_zone__ flag is detected.
return applyArgs[0].data?.['__ignore_ng_zone__'] === true;
}