【问题标题】:Angular Observables: How to optimize subscription / subscribe vs asyncAngular Observables:如何优化订阅/订阅与异步
【发布时间】:2020-12-18 21:31:58
【问题描述】:

我有以下组件:

@Component({
    selector: 'summary-dialog',
    templateUrl: './summary-dialog.component.html',
    styleUrls: ['./summary-dialog.component.css']
})
export class SummaryDialogComponent implements OnInit {

    entriesSummaries: GuiEntrySummary[];
    private interval;

    constructor(private backendService: BackendService,
        private dialogRef: MatDialogRef<SummaryDialogComponent>,
        @Inject(MAT_DIALOG_DATA) private data: {
            eventDbid: number,
            timerState: TimerState,
            entries: IGuiEntry[]
        }) { }

    ngOnInit(): void {
        this.entriesSummaries = this.data.entries.map(entry => new GuiEntrySummary(entry));
        this.getSummaries();

        // automatic refresh the entries summaries every 10 secs
        this.interval = setInterval(() => {
            this.getSummaries();
        }, 10000)

        this.dialogRef.afterClosed().subscribe(() => clearInterval(this.interval));
    }

    private getSummaries(): void {
        this.backendService.getSummaries(this.data.eventDbid, this.data.timerState.elapsedSeconds).subscribe(entriesSummaries =>
            entriesSummaries.forEach(entrySummaries =>
                this.entriesSummaries.find(entry => entry.dbid === entriesSummaries.dbid).summaries = entrySummaries.summaries
            )
        );
    }
}

这个组件是一个对话框。每个条目( data -> entries )都是对话框中的一行。已经解析的条目将被传递到对话框中,这是显示数据的基础。每个条目都将使用一个摘要数组来丰富。 该组件应每 10 秒刷新一次所有条目的摘要。

但我的主要问题是了解如何以可接受的方式实现此用例。

在这种情况下订阅组件真的很糟糕吗?我这样做主要是因为模板应该一直显示现有数据,即使是在从 backendService 获取新数据的短时间内。

如果我以异步方式执行此操作(例如:

<div *ngIf="( getAllEntriesSummaries |async ) as entriesSummaries">

),我想,容器不会在获取新数据的确切时刻显示。此外,我需要在模板中调用另一个函数,它将当前数据的数据与新数据合并?!

使用我当前的解决方案(在组件中可能使用反订阅模式),我有一个非常干净的模板:

<h2 mat-dialog-title>Entries Summaries</h2>
<mat-dialog-content class="mat-typography">

    <div class="grid">

        <div *ngFor="let entry of entriesSummaries; let i = index" class="entry-row"
            [ngClass]="i % 2 === 0 ? 'bg-even' : 'bg-odd'">

            <!--entry row -->

        </div>
    </div>
</mat-dialog-content>

【问题讨论】:

    标签: angular rxjs observable subscribe


    【解决方案1】:

    您可以像这样使用timer RxJS 运算符:

    entriesSummaries$: Observable<GuiEntrySummary[]>;
    
    ngOnInit() {
      this.entriesSummaries$ = timer(0, 10000).pipe(exhaustMap(() => this.getSummaries()));
    }
    
    private getSummaries(): Observable<GuiEntrySummary[]> {
       return this.backendService.getSummaries(this.data.eventDbid, this.data.timerState.elapsedSeconds).pipe(tap(() => /* side effects */));
      }
    
    
     <div *ngIf="(getAllEntriesSummaries$ | async) as entriesSummaries">
    

    来自文档: timer 返回一个 Observable,它发出无限的升序整数序列,时间间隔恒定,您可以在这些发射之间选择。第一次发射发生在指定的到期时间之后。最初的延迟可能是一个日期。默认情况下,此运算符使用 {@link asyncScheduler} {@link SchedulerLike} 来提供时间概念,但您可以将任何 {@link SchedulerLike} 传递给它。如果没有指定 period,则输出 Observable 只会发出一个值,0。否则,它会发出无限序列。

    我不会说在组件中订阅是一种不好的做法,但是,建议使用 async 管道以获得更好的性能,尤其是在您使用 OnPush 更改检测时。

    【讨论】:

    • 好的,但是现在缺少将条目与收到的摘要连接的部分(我的问题中的过滤器部分)。我到底应该在哪里做呢?后端服务仅获取摘要,这些摘要必须与条目摘要相关联。我应该在使用地图的 backendService 调用期间这样做吗?
    • 是的,如果要转换数据,可以使用map 运算符,或者如果要根据条件过滤数据,可以使用filter 运算符,或两者兼而有之。这取决于您的用例。如果您不想更改数据并且只想添加一些副作用,您可以使用类似于forEach 方法的tap 运算符,但请记住tap as forEach 不会'不返回任何东西。查看您的代码,我不确定您想要实现什么
    • 基本上我只想用摘要丰富现有数据。所以我会认为该地图应该是正确的运营商
    • 可能是这样。我仍然对您要实现的功能感到困惑。您可能需要使用 intervalstartWith 运算符,而不是 timer。您传递给startWith 的参数是this.data.entries.map(entry =&gt; new GuiEntrySummary(entry))。如果您需要进一步的帮助,请提供 StackBlitz,我会看看。干杯!
    猜你喜欢
    • 2019-05-06
    • 1970-01-01
    • 2020-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-30
    • 2018-01-13
    • 1970-01-01
    相关资源
    最近更新 更多