为什么组合的可观察对象在使用 Subject 时不更新模板,或者如果它们在 ngAfterContentInit 之后发出

Why do combined observables do not update template when using Subject or if they emit after ngAfterContentInit

我在组件中定义了一个可观察对象 (result$),并通过异步管道将其显示在其模板上。 observable 是其他 2 个 observables(first$,second$)通过 combineLatest 的组合。 如果一个或两个可观察对象发射得太早(在我发现的 ngAfterContentInit 之前),生成的可观察对象将不会发射值。

组件:不工作

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

result$: Observable<number>;
first$ = new Subject<number>();
second$ = new Subject<number>();

  constructor() {}

  ngOnInit(){
      this.result$ = combineLatest(
        this.first$,
        this.second$
      ).pipe(
        map(([first, second]) => {
          // This is not printed to the console
          console.log('combined obs emitted value');
          return first + second;
        })
      );
   console.log('first and second emit value');
   this.first$.next(2);
   this.second$.next(4);
  }

  ngAfterContentInit() {
      console.log('ngAfterContentInit');
  }
}

执行顺序为:

1.first 和第二个发射值

2.ngAfterContentInit

我这里的假设是,在 ngAfterViewInit 中,模板已经呈现并且已经进行了订阅。因为可观察对象在此之前发出一个值,所以不会通知组件。这只能意味着生成的可观察对象是冷的(因此,您需要在它发出值之前订阅)。 2个可观察对象是主题,所以我的假设是主题是冷可观察对象。这是正确的吗?

如果我延迟发出 first$ 和 second$,一切正常: 组件:first$ 和 second$ 稍后发出 @零件({ 选择器:'my-app', templateUrl: './app.component.html', styleUrls: [ './app.component.css' ] }) 导出 class AppComponent {

result$: Observable<number>;
first$ = new Subject<number>();
second$ = new Subject<number>();

  constructor() {}

  ngOnInit(){

      this.result$ = combineLatest(
        this.first$,
        this.second$
      ).pipe(
        map(([first, second]) => {
          console.log('combined obs emitted value');
          return first + second;
        })
      );

    // Solution 1: add timeout
    setTimeout(() => {
      console.log('first and second emit value');
      this.first$.next(2);
      this.second$.next(4);
    })

  }

  ngAfterContentInit() {
      console.log('ngAfterContentInit');
  }
}

现在的订单是:

  1. ngAfterContentInit

  2. 第一个和第二个发射值

  3. 组合 obs 发射值

再说一次,这是因为订阅是在可观察对象发出值之前进行的吗?

如果我将可观察对象更改为 BehaviorSubject,即使值在订阅发生之前发出,一切也会正常进行。这是否意味着 BehaviourSubjects 是热可观察对象? 组件:作品

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

result$: Observable<number>;
first$ = new BehaviorSubject<number>(0);
second$ = new BehaviorSubject<number>(0);

  constructor() {

  }

  ngOnInit(){

this.result$ = combineLatest(
        this.first$,
        this.second$
      ).pipe(
        map(([first, second]) => {
          // This is not printed to the console
          console.log('combined obs emitted value');
          return first + second;
        })
      );


  console.log('first and second emit value');
   this.first$.next(2);
   this.second$.next(4);


  }

  ngAfterContentInit() {
      console.log('ngAfterContentInit');
  }
}

Stackblitz

Q1:2个可观察对象是主体,所以我的假设是主体是冷可观察对象。这是正确的吗?

A1:answer from this question says that the subject itself is hot. This article描述了热、冷和主题。

Q2:再说一次,这是因为订阅是在 observables 发出值之前进行的吗?

A2:是的。订阅 Subject 将在您订阅后收到值。您引入的 delay() 应该为这种情况的发生提供了时间。

为什么? 这是因为 BehaviorSubject 总是保存一个值并在订阅时发出它,而 Subject 不保存值,它只是从生产者向当前订阅者发出值。 .

Q3:这是否意味着 BehaviourSubjects 是热观察对象?

答:见A1。

如果这不能直接回答您的问题,我们深表歉意。我只是想说...在处理 Subject 和 BehaviorSubject 时,我并不关心它们是热的还是冷的。

相反,我问:"Do I want the observable to always hold a value when I subscribe?"。如果是,我使用 BehaviorSubject。如果我没问题,订阅时没有任何价值,那么 Subject 就可以。除了这个区别,它们都将在订阅后收到发出的值。 --- 取决于您的用例。

使用 BehaviorSubjects 而不是 Subjects

https://stackblitz.com/edit/angular-4gnxto?file=src/app/app.component.ts

异步管道在主题发出后进行订阅。 BehaviorSubjects 在您订阅时给出最新结果,Subjects 只在它们发出时给您一个值。

当异步管道订阅使用 combineLatest 生成的 result$ observable 时,它​​不会从已经发出的 Subjects 发出任何值,它只会为订阅后发出的 Subjects 发出 combineLatest。

看看这个 StackBtitz

https://stackblitz.com/edit/angular-eb7dge?file=src/app/app.component.ts

如果您单击发出,则在异步管道进行订阅后发出值。