从组件订阅一个可观察到的两次无效

问题描述

我有几个订阅我的数据服务的组件,它们都工作正常。但是在我的组件之一中,我尝试订阅两次(在ngOnInit和ngAfterViewInit内部),但这不起作用。这是组件:

ngOnInit() {
    this.dataService.data$.pipe(first()).subscribe(subscribetoData => {
        this.title = this.dataService.getData("...");
            this.anotherService.getData
                .subscribe(another => {
                    this.data = data;
                },...
        });
    }

ngAfterViewInit() {
    this.dataService.data$.pipe(first()).subscribe(subscribetoData => {
        let options = {
            data: {
            }
            ...
            {
            title: this.dataService.getData("...");
            },...

        };
        ...

    });
}

如果我从ngOnInit删除订阅,则ngAfterViewInit可以正常工作,否则它将失败。那么,有没有办法从同一组件中同时订阅两次或多次?

这是数据服务:

private dataSource = new ReplaySubject(1);
data$ = this.dataSource.asObservable();

loadData(... : void) {
    if (sessionStorage["data"] == null {
        this.http.request(...)
        .map((response: Response) => response.json()).subscribe(data => {
            ...
            sessionStorage.setItem("data",JSON.stringify(this.data));
            this.dataSource.next(this.data);
            ...
        });
    } else {
        this.dataSource.next(this.data);
    }
}

getData(... : string){
    ...
}

解决方法

代码中的双重订阅没有问题-可能您遇到了一些异步代码问题。浏览器足够快,可以快速浏览组件的生命周期并快速调用ngOnInitngAfterViewInit。它们几乎都将同时执行,并且闪电般快速-绝对比http调用快。在这种情况下,在您的ngOnInit的订阅中,您有另一个可能在ngAfterViewInit之后执行的调用(尽管我不确定)。

下面是一个示例,该示例显示在单个组件中进行双重订阅可以工作: https://stackblitz.com/edit/double-subscribe?file=src/app/app.component.ts

尝试将逻辑重构为更连续的:如果必须在ngAfterViewInit中的所有异步代码完成后 执行ngOnInit,请保存{ {1}}的链在变量中的某个位置;如果您的ngOnInit不关心ngAfterViewInit,请尝试避免访问相同的变量,尤其是ngOnInit

也请尝试避免嵌套this.data-可以将其替换为subscribe / switchMap

flatMap

要重构您的ngOnInit() { this.dataService.data$.pipe( first(),tap(data => this.title = this.dataService.getData(data)),// note that this should be synchronous switchMap(data => { // another asynchronous call here return this.anotherService.getData(data) }) ).subscribe(finalData => { this.data = finalData } 以在 ngAfterViewInit之后执行,请执行以下操作:

ngOnInit

通常,您会想想为什么甚至需要onInitData$: Observable<any>; ngOnInit() { this.onInitData$ = this.dataService.data$.pipe( first(),// note that this should be synchronous switchMap(data => { // another asynchronous call here return this.anotherService.getData(data) }),shareReplay(1) // shareReplay(1) is important to avoid doing double http requests per subscribe ); this.onInitData$.subscribe(data => console.log('data from ngOnInit',data)); } ngAfterViewInit() { this.onInitData$.pipe(switchMap(thatData) => { // will be executed only AFTER the ngOnInit is done return this.dataService.data$.pipe(first()).subscribe(subscribeToData => { let options = { data: { } ... { title: this.dataService.getData("..."); },... }; ... }); }).subscribe(dataFromAfterViewInit => {}) } ?您想通过在ngAfterViewInit / onInit之间拆分这些调用来实现什么?他们为什么要访问组件中的相同数据?