从一个组件订阅一个 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){
...
}
您的代码中的双重订阅没有问题 - 可能您遇到了一些异步代码问题。浏览器足够快,可以快速完成组件的生命周期并快速调用 ngOnInit
和 ngAfterViewInit
。它们都将几乎同时执行并且快如闪电——绝对比 http 调用快。在这种情况下,在您的 ngOnInit
订阅中,您有另一个调用可能会在 之后 ngAfterViewInit
执行(尽管我不确定)。
这是一个示例,说明在单个组件中双订阅有效:
https://stackblitz.com/edit/double-subscribe?file=src/app/app.component.ts
尝试重构您的逻辑以使其更加连续:如果您的 ngAfterViewInit
必须在 之后 执行 ngOnInit
中的所有异步代码都已完成 - 存储ngOnInit
链在某个变量中的结果;如果您的 ngAfterViewInit
不关心 ngOnInit
尽量避免访问相同的变量,尤其是 this.data
.
同时尽量避免嵌套 subscribe
s - 它们可以替换为 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
之间拆分这些调用,您想实现什么?为什么他们访问组件中的相同数据?
我有几个组件订阅了我的数据服务,它们都工作正常。但是在我的一个组件中,我尝试订阅两次(在 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){
...
}
您的代码中的双重订阅没有问题 - 可能您遇到了一些异步代码问题。浏览器足够快,可以快速完成组件的生命周期并快速调用 ngOnInit
和 ngAfterViewInit
。它们都将几乎同时执行并且快如闪电——绝对比 http 调用快。在这种情况下,在您的 ngOnInit
订阅中,您有另一个调用可能会在 之后 ngAfterViewInit
执行(尽管我不确定)。
这是一个示例,说明在单个组件中双订阅有效: https://stackblitz.com/edit/double-subscribe?file=src/app/app.component.ts
尝试重构您的逻辑以使其更加连续:如果您的 ngAfterViewInit
必须在 之后 执行 ngOnInit
中的所有异步代码都已完成 - 存储ngOnInit
链在某个变量中的结果;如果您的 ngAfterViewInit
不关心 ngOnInit
尽量避免访问相同的变量,尤其是 this.data
.
同时尽量避免嵌套 subscribe
s - 它们可以替换为 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
之间拆分这些调用,您想实现什么?为什么他们访问组件中的相同数据?