首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >OnInit与异步管道之间的时序问题

OnInit与异步管道之间的时序问题
EN

Stack Overflow用户
提问于 2020-07-24 13:45:08
回答 2查看 417关注 0票数 1

我对async-pipe和Observable有一个问题,后者在OnInit中获得了它的第一个值。必须是关于OnInit发生的时间点和模板呈现的时间点的时间问题,因此Observable被订阅。

考虑这一构成部分:

代码语言:javascript
复制
export class AppComponent implements OnInit {

    subjectA$: Subject<{name:string}>;
    subjectB$: Subject<{name:string}>;

    constructor(
        protected http: HttpClient
    ) {
    }

    ngOnInit() {
        this.subjectA$ = new Subject<{name: string}>();
        this.subjectA$.next({name: "A"});

        this.subjectB$ = new Subject<{name: string}>();
        setTimeout(() => {
          this.subjectB$.next({name: "B"});
        }, 0);
    }

}

模板:

代码语言:javascript
复制
<p *ngIf='subjectA$ | async as subjectA; else: nosubjectA'>
  subjectA: {{subjectA.name}}
</p>

<ng-template #nosubjectA>
  <p>no subjectA</p>
</ng-template>

<p *ngIf='subjectB$ | async as subjectB; else: nosubjectB'>
  subjectB: {{subjectB.name}}
</p>

<ng-template #nosubjectB>
  <p>no subjectB</p>
</ng-template>

这会导致

代码语言:javascript
复制
no subjectA

subjectB: B 

这意味着:即使subjectA$onInit中获得了一个值,视图也不会更新。如果像您在setTimeout中看到的那样,围绕在subjectB$中创建第一个值,它就会工作,我就会看到这个值。虽然这是一个解决方案,但我想知道为什么会发生这种情况,还有更好的解决方案吗?

我已经找到的一个解决方案是使用BehaviorSubject而不是提供第一个值作为初始值:

代码语言:javascript
复制
        this.subjectC$ = new BehaviorSubject<{name: string}>({name: "C"});

使用类似的subjectC模板引出subjectC。

在StackBlitz上试一试。

我真正可以观察到的一点都不是Subject,而是一个不同内容的combineLatest-call的结果,其中只有一个是(不幸的是,因为它使用的是来自@Input()-annotation的值) Subject,并且在OnInit中使用next手动推送,就像在示例中一样。其余的来自http等人。很可能我可以将合并的结果封装在一个BehaviourSubject中,但对我来说,这似乎是丑陋和危险的,因此它甚至比setTimeout方法更糟糕。但我敢打赌一定有人能帮我找到一个真正有用的解决方案。此外,我更愿意避免使用BehaviorSubject,以防止开发人员尝试使用getValue

看斯塔克布利茨

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-07-27 14:49:39

在我发表评论之后,我真的忍不住认为一定有一个更好的方法--终于想到了一些有用的东西!

我只是稍微修改了一下你的堆栈闪电战。

代码语言:javascript
复制
private valueA = "A";
private valueB = "B";

subjectA$ = of({ name: this.valueA });
subjectB$ = of({ name: this.valueB });
subjectC$ = combineLatest([this.subjectA$, this.subjectB$])
          .pipe(
            map((things: [{name:string}, {name:string}]): {name:string} => {return {name: things.map(x => x.name).join('|')}})
          );

这样,我们甚至可以放弃ngOnInit钩子,而且所有的事情都应该如此!

票数 1
EN

Stack Overflow用户

发布于 2020-07-24 13:54:51

一个快速的解决方法是将ReplaySubject与缓冲区1一起使用,而不是使用BehaviorSubject。您不必提供默认值,而且它既没有getValue()函数,也没有value getter。然而,它会缓冲(或保存)最后发出的值,并在新订阅时立即释放它。

尝试以下几点

代码语言:javascript
复制
ngOnInit() {
  this.subjectA$ = new ReplaySubject<{name: string}>(1);
  this.subjectA$.next({name: "A"});

  this.subjectB$ = new ReplaySubject<{name: string}>(1);
  this.subjectB$.next({name: "B"});

  this.subjectC$ = combineLatest([this.subjectA$, this.subjectB$]).pipe(
    map((things: [{name:string}, {name:string}]): {name:string} => {
      return {name: things.map(thing => thing.name).join('|')}
    })
  );
}

我修改了你的斯塔克布利茨

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63074857

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档