AsyncPipe initial value null only if subscription is not shared

若如初见. 提交于 2019-12-24 18:34:58

问题


Given a template that looks like something like this

<some-component
  *ngIf="someColdObservable$ | async"
  [result]="someColdObservable$ | async"
></some-component>

and an observable that looks like this:

someColdObservable$: this.store.pipe(
  select(isAllowedToDoThis),
  filter(Boolean),
  flatMap(() => apiRequest())
);

The someColdObservable$ gets subscribed to twice (as expected), which in turn issues two api calls (this is obviously a code smell, but let's disregard that at the moment).

In this scenario some-component does not contain any null checks, and AsyncPipe will return null if apiRequest has not emitted a value before the AsyncPipe is evaluated in the template resulting in some-component throwing a cannot access x of null (as [result] is still null at this point) see AsyncPipe source for reference.

This is all expected behaviour (or at least after reading the source) however, when I try to mitigate the issue with making two requests by adding the shareReplay to someColdObservable$ I also fix the issue of [result] being null before the apiRequest() emits a value. This makes little sense to me as I would expect AsyncPipe to still return a null _latestValue here, leaving the cannot access x of null error unfixed. But for some reason adding shareReplay fixes both of the aforementioned issues.

This is similar to Angular observable behavior odd when shared however, there is still the unanswered question of why shareReplay fixes the issue.

Would someone be able to point out what I am missing here and why AsyncPipe no longer returns null before apiRequest() emits a value?

Appreciate any pointers and input, thanks!


回答1:


First scenario:

  1. the ngIf expression is evaluated. It subscribes to the observable and evaluates to null. So the [result] expression is not evaluated since the ngIf expression is falsy.
  2. the observable emits, and the ngIf expression becomes truthy. So the [result] expression is evaluated. Its own async pipe subscribes to the cold observable which sends another request, and evaluates to null, which is thus passed as input to the component.

Second scenario:

  1. the ngIf expression is evaluated. It subscribes to the observable and evaluates to null. So the [result] expression is not evaluated since the ngIf expression is falsy.
  2. the observable emits, and the ngIf expression becomes true. So the [result] expression is evaluated. Its own async pipe subscribes to the hot observable which immediately replays what is has last emitted, and doesn't send a request anymore. The expression is thus evaluated to a non-null value, which is passed as input to the component.

A better solution than using shareReplay would be to store the result of the async pipe in a variable and use it:

<some-component
  *ngIf="someColdObservable$ | async as result"
  [result]="result"
></some-component>


来源:https://stackoverflow.com/questions/59109448/asyncpipe-initial-value-null-only-if-subscription-is-not-shared

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!