Angular Observable首先需要完成 [英] Angular Observable need to complete first
问题描述
@Input() list: string[];
ngOnInit() : void {
this.valueMap = new Map<any, any>();
this.getDataFromService();
this.buildContainer();
}
private getDataFromService(): void {
this.list.forEach(value-> {
this.fetchService(value).subscribe(data ->{
this.valueMap.set(value,data);
}
)
})
}
private buildContainer(): void {
console.log(this.valueMap.size); // shows always 0 even when service returns the data
}
现在,我必须在方法buidlContainer()
中使用此valueMap
,
因此,我需要先从服务中获取完整的地图.当我在buildConatainer()中使用该映射时,它向我显示了undefined.
Now thing is that I have to use this valueMap
in the method buidlContainer()
,
hence I need to get the complete map first from service. And when I use that map in buildConatainer() it shows me undefined .
我了解到这是一些异步调用问题.另外,我不能为.subscribe()中的每个值调用buildContainer()方法,因为这不是更好的主意. 因此,我必须先计算Map,然后再进行进一步处理.
I understood that this is some async call issue . Also , I can not call method buildContainer() for each value in .subscribe() as that would not be better idea. So , I have to compute the Map first before processing that further..
我们将不胜感激,并且我无法将服务从可观察的"返回承诺"返回
Any help is appreciated and i can not modify the service from returning Observable to return Promise
我要执行以下操作
private buildContainer(): void {
for([key,data] of this.valueMap) {
-----some code----
}
}
推荐答案
这是向代码中引入反应性范例的缺点之一(或优点取决于您的看法).反应过程的单点将逐渐或部分或完全爬入代码的其他部分.
This is one of the disadvantage (or advantage depending how you look at it) of introducing reactive paradigm to your code. A single point of reactive procedure would gradually creep into other parts of the code either partially or completely.
因此,我建议您也使valueMap
具有反应性,并使其根据getDataFromService()
服务中的更改做出响应.一种方法是将valueMap
变量设置为RxJS ReplaySubject
带有1的缓冲区.因此它将缓冲(或保留)它的最后一个值,并在订阅后立即将其发出.
So I'd recommend you to make the valueMap
reactive as well and make it respond based on the changes in getDataFromService()
service. One way would to make the valueMap
variable a RxJS ReplaySubject
with a buffer of 1. So it'll buffer (or hold) it's last value pushed to it and emit it immediately upon subscription.
尝试以下
import { forkJoin, ReplaySubject, Subject } from 'rxjs';
import { take, takeUntil } from 'rxjs/operators';
valueMap: ReplaySubject<Map<any, any>> = ReplaySubject<Map<any, any>>(1);
completed$ = new Subject<any>();
@Input() list: string[];
ngOnInit(): void {
this.valueMap = new Map <any, any>();
this.getDataFromService();
this.buildContainer();
}
private getDataFromService(): void {
forkJoin(this.list.map(value => this.fetchService(value))).pipe(
takeUntil(this.completed$) // <-- close subscription upon component `ngOnDestroy` hook trigger
).subscribe(
data => {
let map = new Map<any, any>();
data.forEach((item, i) => {
map.set(this.list[i], item);
});
this.valueMap.next(map);
}
);
}
private buildContainer(): void {
this.valueMap.asObservable().pipe(
take(1) // <-- emit only the first notification and complete
).subscribe(
data => {
console.log(this.valueMap.size);
}
);
}
ngOnDestroy() {
this.completed$.next();
this.completed$.complete();
}
我还使用了RxJS forkJoin()
函数结合多个可观察对象,并 takeUntil
运算符在销毁组件时关闭订阅
I've also used RxJS forkJoin()
function to combine multiple observables and takeUntil
operator close the subscription when the component is destroyed.
工作示例: Stackblitz
在示例中,我使用了 jsonplaceholder
API在fetchService()
功能.
In the example, I've used jsonplaceholder
API to mock some HTTP calls in the fetchService()
function.
注意:
-
仅当所有可观察物都完成时,
forkJoin()
才会发射.如果要处理数据流,则必须用combineLatest
或zip
The
forkJoin()
will emit only if all the observables complete. If you're dealing with a stream of data, you will have to replace it with eithercombineLatest
orzip
based on your requirement.
一个明显的问题是,如果快速连续多次调用buildContainer()
函数,则每次都会触发单独的订阅.
There is an obvious issue where if the buildContainer()
function is called multiple times in quick successions, each will trigger individual subscriptions.
这篇关于Angular Observable首先需要完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!