From 707bfc9b326f321f302dccfdfebef7380c914eb5 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Wed, 21 Feb 2024 16:26:42 -0800 Subject: [PATCH] perf(common): `AsyncPipe` should not call `markForCheck` on subscription (#54554) This commit prevents `AsyncPipe` from calling `markForCheck` when values are synchronously emit during subscription to an observable. This prevents subscriptions to `Replay` observables from needlessly walking up to the root of the view tree during template execution for each new replay observable in the template. PR Close #54554 --- packages/common/src/pipes/async_pipe.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/common/src/pipes/async_pipe.ts b/packages/common/src/pipes/async_pipe.ts index b8b54ac7ce5..0f51b8aa44b 100644 --- a/packages/common/src/pipes/async_pipe.ts +++ b/packages/common/src/pipes/async_pipe.ts @@ -102,6 +102,7 @@ const _subscribableStrategy = new SubscribableStrategy(); export class AsyncPipe implements OnDestroy, PipeTransform { private _ref: ChangeDetectorRef | null; private _latestValue: any = null; + private markForCheckOnValueUpdate = true; private _subscription: Unsubscribable | Promise | null = null; private _obj: Subscribable | Promise | EventEmitter | null = null; @@ -134,7 +135,15 @@ export class AsyncPipe implements OnDestroy, PipeTransform { transform(obj: Observable | Subscribable | Promise | null | undefined): T | null { if (!this._obj) { if (obj) { - this._subscribe(obj); + try { + // Only call `markForCheck` if the value is updated asynchronously. + // Synchronous updates _during_ subscription should not wastefully mark for check - + // this value is already going to be returned from the transform function. + this.markForCheckOnValueUpdate = false; + this._subscribe(obj); + } finally { + this.markForCheckOnValueUpdate = true; + } } return this._latestValue; } @@ -181,9 +190,9 @@ export class AsyncPipe implements OnDestroy, PipeTransform { private _updateLatestValue(async: any, value: Object): void { if (async === this._obj) { this._latestValue = value; - // Note: `this._ref` is only cleared in `ngOnDestroy` so is known to be available when a - // value is being updated. - this._ref!.markForCheck(); + if (this.markForCheckOnValueUpdate) { + this._ref?.markForCheck(); + } } } }