Skip to content

Commit 6433cd0

Browse files
authored
perf(angular): do not run change detection when finishing transaction (#3622)
1 parent e44bc63 commit 6433cd0

File tree

1 file changed

+30
-3
lines changed

1 file changed

+30
-3
lines changed

packages/angular/src/tracing.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@ import { logger, stripUrlQueryAndFragment, timestampWithMs } from '@sentry/utils
66
import { Observable, Subscription } from 'rxjs';
77
import { filter, tap } from 'rxjs/operators';
88

9+
// That's the `global.Zone` exposed when the `zone.js` package is used.
10+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
11+
declare const Zone: any;
12+
13+
// There're 2 types of Angular applications:
14+
// 1) zone-full (by default)
15+
// 2) zone-less
16+
// The developer can avoid importing the `zone.js` package and tells Angular that
17+
// he is responsible for running the change detection by himself. This is done by
18+
// "nooping" the zone through `CompilerOptions` when bootstrapping the root module.
19+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
20+
const isNgZoneEnabled = typeof Zone !== 'undefined' && !!Zone.current;
21+
922
let instrumentationInitialized: boolean;
1023
let stashedStartTransaction: (context: TransactionContext) => Transaction | undefined;
1124
let stashedStartTransactionOnLocationChange: boolean;
@@ -93,13 +106,27 @@ export class TraceService implements OnDestroy {
93106
filter(event => event instanceof NavigationEnd),
94107
tap(() => {
95108
if (this._routingSpan) {
96-
this._routingSpan.finish();
97-
delete this._routingSpan;
109+
if (isNgZoneEnabled) {
110+
// The `Zone.root.run` basically will finish the transaction in the most parent zone.
111+
// The Angular's zone is forked from the `Zone.root`. In this case, `zone.js` won't
112+
// trigger change detection, and `ApplicationRef.tick()` will not be run.
113+
// Caretaker note: we're using `Zone.root` except `NgZone.runOutsideAngular` since this
114+
// will require injecting the `NgZone` facade. That will create a breaking change for
115+
// projects already using the `TraceService`.
116+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
117+
Zone.root.run(() => {
118+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
119+
this._routingSpan!.finish();
120+
});
121+
} else {
122+
this._routingSpan.finish();
123+
}
124+
this._routingSpan = null;
98125
}
99126
}),
100127
);
101128

102-
private _routingSpan?: Span;
129+
private _routingSpan: Span | null = null;
103130
private _subscription: Subscription = new Subscription();
104131

105132
public constructor(private readonly _router: Router) {

0 commit comments

Comments
 (0)