Angular Zone seems to have problems with the new rxjs firstValueFrom leading to infinite dirty check loop

This issue has been created since 2022-09-19.

Which @angular/* package(s) are the source of the bug?

core, zone.js

Is this a regression?



See reproduction:

Please provide a link to a minimal reproduction of the bug

Please provide the exception or error you saw

Infinite load. No error.

Please provide the environment you discovered this bug in (run ng version)

Angular CLI: 14.0.7
Node: 16.16.0
Package Manager: npm 8.11.0
OS: darwin arm64

Angular: 14.2.2
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router

Package                         Version
@angular-devkit/architect       0.1402.3
@angular-devkit/build-angular   14.2.3
@angular-devkit/core            14.2.3
@angular-devkit/schematics      14.0.7
@angular/cli                    14.0.7
@schematics/angular             14.0.7
rxjs                            7.5.6
typescript                      4.7.4

Anything else?

No response

alxhub wrote this answer on 2022-09-19

Angular is working as expected here, although I can definitely understand the confusion.

The binding in the template uses a function call to create the Observable being subscribed to:

<hello *ngIf="angularHurtsMe() | async as a">

This function returns a new Observable every time it's called.

angularHurtsMe(): Observable<string> {
  return from(firstValueFrom(of('AAAAAAHHHHHHHH!!!!!')));

My suspicion is that firstValueFrom internally does a setTimeout or Promise.resolve or some other operation which schedules a new round of change detection. Thus the order of events is:

  • CD runs and calls angularHurtsMe() as per the binding.
  • async subscribes to the Observable.
  • firstValueFrom somehow triggers a new round of CD.
  • CD runs and calls angularHurtsMe() again, creating a new Observable.
  • async pipe unsubscribes from the old Observable and subscribes to the new one.
  • firstValueFrom runs again, triggering another round of CD.
  • Repeat ad infinitum.

The problem here is the use of a function call in a binding which is not idempotent - a common anti-pattern in Angular. Most of the time this results in ExpressionChangedAfterItHasBeenChecked, but in this case the behavior of firstValueFrom seems to result in infinite change detection instead.

The solution is to only create the Observable once:

angularHurtsMeNoMore = from(firstValueFrom(of('AAAAAAHHHHHHHH!!!!!')));

Binding to this Observable with the async pipe works as expected and doesn't result in infinite CD.

CodeBast4rd wrote this answer on 2022-09-19

Before opening the Issue I checked the implementation of firstValueFrom:

And the only action they take is creating a new Promise which should in my Opinion not trigger a new change detection round. As the newly created Promise is a child Promise or microZoneTask.

The code in production was more similar to;
originSubject = new BehaviorSubject('I am the Origin');

async originOfPain(): Promise {
const originString = await firstValueFrom(this.originSubject);
return originString;

see the previously linked adjusted example.

And in the async await context I expect the Promises to behave like a then chain. Which should as I understand zone.js not trigger a global change detection if a new Promise is created in the Promise.then context.

Or are new Promise() polluting the global Promise handling as setTimeouts?

JoostK wrote this answer on 2022-09-19

Promises always resolve in a new microtick, so lastValueFrom will not be able to produce its value in the same microtick and therefore a new change detection run is being scheduled once the promise eventually resolves.

CodeBast4rd wrote this answer on 2022-09-19

But why is the firstValueFrom not part of the Parent Promise chain? The Promise chain should be resolved in the same tick as an immediate Context Que.

See for example:


JoostK wrote this answer on 2022-09-19

That StackBlitz does not behave like NodeJS; running in a StackBlitz webcontainer does show the expected outcome:


firstValueFrom is executed during a change detection cycle, but the resulting Promise will not resolve until the event loop has processed the accompanying microtick. Consequently, change detection has to rerun once the resolved becomes available, but that's only in a new microtick.

CodeBast4rd wrote this answer on 2022-09-21

@JoostK thanks for the further explanation. I checked also with only using one promise and older zone.js. I forgot it was a common behaviour of zone.js with promises because it does no longer occur with observables.

What I don't understand is how the changeDetection: ChangeDetectionStrategy.OnPush does not resolve the issue when the component should be only checked on new inputs.

More Details About Repo
Owner Name angular
Repo Name angular
Full Name angular/angular
Language TypeScript
Created Date 2014-09-18
Updated Date 2022-09-30
Star Count 84080
Watcher Count 3064
Fork Count 22235
Issue Count 1197


Issue Title Created Date Comment Count Updated Date
xClusterNetwork: AddressMask as key property prevents multiple cluster networks in a config 1 2021-09-20 2022-08-09
Unicode URL text, such as ￸'Ασφάλεια Υγείας' detection 2 2021-09-09 2022-05-04
Shinymanager conflicting with Quanteda (again) 5 2021-05-31 2022-08-20
Map: add controls: center on location, set marker 3 2021-12-20 2022-08-23
[Bug]: Browser hang on My Account Page 19 2022-08-02 2022-09-15
docker support? 1 2021-05-03 2022-01-15
Purpose of num_test argument in TimePDE 0 2022-06-21 2022-09-23
After wiping folders on an SD with Bleachbit, the files contained in there are still recoverable with RescuePro. 1 2021-08-12 2022-09-08
System Error when running Ganache 2.5.4 on win32 0 2022-01-24 2022-01-31
Click "User" to display the following error, please answer 1 2022-04-15 2022-09-15
[INTEGRATION][Flink] Move OpenLineageFlinkJobListener from sample application to root package 2 2022-02-24 2022-09-24
[INTEGRATION][Airflow] Add a facet to capture job/run dependencies (a task is dependent on another task in the DAG) 2 2022-02-18 2022-09-25
Improve ArrayContraction printing to NumPy so that it can handle contractions of a single Array 5 2021-10-14 2022-09-25
Selector list is always hidden in overflow 0 2021-10-05 2022-07-31
Tooltip body doesn't update with reactive changes 3 2021-10-06 2022-07-31
Mobileconfig fails to be installed on the iPhone, 0 2022-08-11 2022-09-02
Weekly Digest (20 March, 2021 - 27 March, 2021) 0 2021-03-26 2022-09-16
Hoisted functions inside block statements are inadvertently removed, even when used 3 2022-03-06 2022-09-22
Broadcasting doesn't respect scalar coordinates 1 2021-12-13 2022-09-17
How to create a scrollbar in OpenSeaDragon canvas ? 2 2022-08-30 2022-09-22
[ ENH ] Refactor the Sensor Download Service Class to the latest pattern (Rev 3) 0 2021-08-14 2022-09-22
[ ENH ] Refactor the Firewall Policies Service Class to the latest pattern (Rev 3) 0 2021-08-15 2022-09-26
[ ENH ] Refactor the Quick Scan Service Class to the latest pattern (Rev 3) 0 2021-08-14 2022-09-30
[ ENH ] Refactor the Recon Service Class to the latest pattern (Rev 3) 0 2021-08-15 2022-09-06
[ ENH ] Refactor the MalQuery Service Class to the latest pattern (Rev 3) 0 2021-08-15 2022-09-12
[BUG]fail to exec "go test" in some package 2 2021-12-29 2022-08-28
File loader always uses process.cwd(), fails to find seeds and factories in monorepos with globs 0 2021-03-21 2022-09-14
Where to set the batchsize while training timesformer? 6 2021-12-15 2022-09-22
Component options for textbox 1 2021-09-16 2022-09-25
alpine cannot build rust agent 3 2019-12-05 2022-09-16
Mention flood protection 1 2022-06-23 2022-08-23
Changelog for UE recent update? 2 2022-06-22 2022-09-22
[Bug]: Dashboard crashes 2 2022-05-20 2022-09-23
[Keyboard navigation - Adaptive Cards - Host Config]: Keyboard focus jumps to the Background after activating the Host Config button. 2 2022-02-08 2022-09-12
can't set my website to cross-origin isolated 0 2021-12-17 2022-09-02
Nonroad underestimates Diesel PM from T4 engines 0 2021-07-20 2022-07-28
DtLessThanMin when solving Mixed Difference Differential Equations test example 1 2022-09-05 2022-09-23
feature request: make remake take partial parameter maps 0 2022-09-05 2022-09-23
Content doesn't fit badge indicator 1 2022-04-21 2022-09-29
The Xray IDE plugin should support ruby/rails. 0 2021-11-24 2021-12-31
Menu/Item API: 1-based depth vs. 0-based level 8 2021-01-08 2022-09-22
canal 1.1.5 被检测fastjson漏洞、Jackson-databind 反序列化漏洞 2 2021-09-09 2022-08-16
SDL schema doesn't include InputObject when struct also have SimpleObject 2 2021-12-01 2022-09-25
freqtrade does not create_trade for pair when dataframe['buy']= 1.0 0 2021-10-12 2022-09-11
pass data between 2 screens 1 2019-06-15 2022-09-26
Block imports to unsupported libraries in web tests 0 2022-08-01 2022-09-27
Unifi Protect delay sensor 'Detected Object' 6 2022-02-19 2022-07-27
Producer and Consumer fail logs 3 2021-11-11 2022-09-21 - false positive 1 2022-03-31 2022-09-24
Add Restart action to Deployment details page 1 2021-11-27 2022-09-19