Skip to content

Commit

Permalink
Run async stuff outside Angular
Browse files Browse the repository at this point in the history
This commit wraps the tracking functionality to run outside of the Angular zone. Previously, it hindered
server-side rendering and hydration, causing instability in the app. The app achieves stability when no
microtasks or macrotasks are running.

On the client side, this change also prevents unnecessary view updates when asynchronous tasks are set
up by `trackPageView`.
  • Loading branch information
arturovt committed Nov 13, 2024
1 parent 643ffcb commit 6bd886c
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Subscription } from "rxjs";
import { AnalyticsPlugin } from "@microsoft/applicationinsights-analytics-js";
import {objDeepFreeze} from "@nevware21/ts-utils";
import { PropertiesPlugin } from "@microsoft/applicationinsights-properties-js";
import { runOutsideAngular } from "./run-outside-angular";

interface IAngularExtensionConfig {
/**
Expand Down Expand Up @@ -113,7 +114,7 @@ export class AngularPlugin extends BaseTelemetryPlugin {
const pageViewTelemetry: IPageViewTelemetry = {
uri: _angularCfg.router.url
};
_self.trackPageView(pageViewTelemetry);
runOutsideAngular(() => _self.trackPageView(pageViewTelemetry));
}

// subscribe to new router events
Expand All @@ -129,7 +130,7 @@ export class AngularPlugin extends BaseTelemetryPlugin {
uri: _angularCfg.router.url,
properties: { duration: 0 } // SPA route change loading durations are undefined, so send 0
};
_self.trackPageView(pvt);
runOutsideAngular(() => _self.trackPageView(pvt));
}
}
});
Expand All @@ -150,7 +151,7 @@ export class AngularPlugin extends BaseTelemetryPlugin {
_propertiesPlugin.context.telemetryTrace.traceID = generateW3CId();
_propertiesPlugin.context.telemetryTrace.name = location && location.pathname || "_unknown_";
}
_analyticsPlugin.trackPageView(pageView);
runOutsideAngular(() => _analyticsPlugin.trackPageView(pageView));
} else {
_throwInternal(_self.diagLog(),
// eslint-disable-next-line max-len
Expand Down Expand Up @@ -186,7 +187,7 @@ export class AngularPlugin extends BaseTelemetryPlugin {
* @param event The event that needs to be processed
*/
processTelemetry(event: ITelemetryItem, itemCtx?: IProcessTelemetryContext) {
this.processNext(event, itemCtx);
runOutsideAngular(() => this.processNext(event, itemCtx));
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* The function that does the same job as `NgZone.runOutsideAngular`.
* It doesn't require an injection context to be specified.
*
* ⚠️ Note: Most of the Application Insights functionality called from
* inside the Angular execution context must be wrapped in this function.
* Angular's rendering relies on asynchronous tasks being scheduled within
* its execution context.
* Since the plugin schedules tasks that do not interact with Angular's rendering,
* it may prevent Angular from functioning reliably. Consequently, it may disrupt
* processes such as server-side rendering or client-side hydration.
*/
export function runOutsideAngular<T>(callback: () => T): T {
// Running the `callback` within the root execution context enables Angular
// processes (such as SSR and hydration) to continue functioning normally without
// timeouts and delays that could affect the user experience.
return typeof Zone !== 'undefined' ? Zone.root.run(callback) : callback();
}

0 comments on commit 6bd886c

Please sign in to comment.