从一个组件订阅一个 observable 两次是行不通的

Subscribe to one observable twice from a component doesn't work

我有几个组件订阅了我的数据服务,它们都工作正常。但是在我的一个组件中,我尝试订阅两次(在 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 中的所有异步代码都已完成 - 存储ngOnInit 链在某个变量中的结果;如果您的 ngAfterViewInit 不关心 ngOnInit 尽量避免访问相同的变量,尤其是 this.data.

同时尽量避免嵌套 subscribes - 它们可以替换为 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
    }

要重构要在 ngOnInit 之后执行的 ngAfterViewInit,请执行以下操作:

onInitData$: Observable<any>;

ngOnInit() {
  this.onInitData$ = 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)
      }),
      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/afterViewInit 之间拆分这些调用,您想实现什么?为什么他们访问组件中的相同数据?