使用 concatMap 时重复调用
Duplicate calls when using concatMap
我想在视图中显示用户注册的下一个即将发生的事件。
为此,我需要首先检索用户注册的最近事件(及时),然后检索该事件的信息。
用户注册的事件列表是动态的,我需要连续使用两个 Observable 的事件信息也是动态的。
所以我尝试使用 concatMap,但我可以看到 getEvent 函数被调用了 11 次...我不明白为什么以及如何才能做得更好。
这是我的控制器
//Controller
nextEvent$: Observable<any>;
constructor(public eventService: EventService) {
console.log('HomePage constructor');
}
ngOnInit(): void {
// Retrieve current user
this.cuid = this.authService.getCurrentUserUid();
this.nextEvent$ = this.eventService.getNextEventForUser(this.cuid);
}
EventService(包含调用 11 次的 getEvent 函数)
// EventService
getEvent(id: string, company?: string): FirebaseObjectObservable<any> {
let comp: string;
company ? comp = company : comp = this.authService.getCurrentUserCompany();
console.log('EventService#getEvent - Getting event ', id, ' of company ', comp);
let path = `${comp}/events/${id}`;
return this.af.object(path);
}
getNextEventForUser(uid: string): Observable<any> {
let company = this.authService.getCurrentUserCompany();
let path = `${company}/users/${uid}/events/joined`;
let query = {
orderByChild: 'timestampStarts',
limitToFirst: 1
};
return this.af.list(path, { query: query }).concatMap(event => this.getEvent(event[0].id));
}
最后是我的观点
<ion-card class="card-background-image">
<div class="card-background-container">
<ion-img src="sports-img/img-{{ (nextEvent$ | async)?.sport }}.jpg" width="100%" height="170px"></ion-img>
<div class="card-title">{{ (nextEvent$ | async)?.title }}</div>
<div class="card-subtitle">{{ (nextEvent$ | async)?.timestampStarts | date:'fullDate' }} - {{ (nextEvent$ | async)?.timestampStarts | date:'HH:mm' }}</div>
</div>
<ion-item>
<img class="sport-icon" src="sports-icons/icon-{{(nextEvent$ | async)?.sport}}.png" item-left>
<h2>{{(nextEvent$ | async)?.title}}</h2>
<p>{{(nextEvent$ | async)?.sport | hashtag}}</p>
</ion-item>
<ion-item>
<ion-icon name="navigate" isActive="false" item-left small></ion-icon>
<h3>{{(nextEvent$ | async)?.location.main_text}}</h3>
<h3>{{(nextEvent$ | async)?.location.secondary_text}}</h3>
</ion-item>
<ion-item>
<ion-icon name="time" isActive="false" item-left small></ion-icon>
<h3>{{(nextEvent$ | async)?.timestampStarts | date:'HH:mm'}} - {{(nextEvent$ | async)?.timestampEnds | date:'HH:mm'}}</h3>
</ion-item>
</ion-card>
RXJS 的 concatMap
方法将所有事件扁平化为一个可观察对象。您最好使用 switchMap
方法。 switchMap 仅订阅最新的可观察对象。
所以这样做可能会解决您的问题:
之前:
return this.af.list(path, { query: query }).concatMap(event => this.getEvent(event[0].id));
之后:
return this.af.list(path, { query: query }).switchMap(event => event);
this.af.list(path, { query: query }).concatMap(event => this.getEvent(event[0].id))
是感冒Observable
。这意味着每次你对其执行订阅时,它都会重新执行底层流,也就是重新调用getEvent
方法。
async
隐式订阅 Observable
,这就是为什么如果您在模板中计算 (nextEvent$ | async)
调用,您将看到 11 来自哪里。
&tldr;
您需要分享对流的订阅:
this.nextEvent$ = this.eventService.getNextEventForUser(this.cuid)
// This shares the underlying subscription between subscribers
.share();
以上将在第一次订阅时连接流,但随后将在所有订阅者之间共享该订阅。
我想在视图中显示用户注册的下一个即将发生的事件。
为此,我需要首先检索用户注册的最近事件(及时),然后检索该事件的信息。
用户注册的事件列表是动态的,我需要连续使用两个 Observable 的事件信息也是动态的。
所以我尝试使用 concatMap,但我可以看到 getEvent 函数被调用了 11 次...我不明白为什么以及如何才能做得更好。
这是我的控制器
//Controller
nextEvent$: Observable<any>;
constructor(public eventService: EventService) {
console.log('HomePage constructor');
}
ngOnInit(): void {
// Retrieve current user
this.cuid = this.authService.getCurrentUserUid();
this.nextEvent$ = this.eventService.getNextEventForUser(this.cuid);
}
EventService(包含调用 11 次的 getEvent 函数)
// EventService
getEvent(id: string, company?: string): FirebaseObjectObservable<any> {
let comp: string;
company ? comp = company : comp = this.authService.getCurrentUserCompany();
console.log('EventService#getEvent - Getting event ', id, ' of company ', comp);
let path = `${comp}/events/${id}`;
return this.af.object(path);
}
getNextEventForUser(uid: string): Observable<any> {
let company = this.authService.getCurrentUserCompany();
let path = `${company}/users/${uid}/events/joined`;
let query = {
orderByChild: 'timestampStarts',
limitToFirst: 1
};
return this.af.list(path, { query: query }).concatMap(event => this.getEvent(event[0].id));
}
最后是我的观点
<ion-card class="card-background-image">
<div class="card-background-container">
<ion-img src="sports-img/img-{{ (nextEvent$ | async)?.sport }}.jpg" width="100%" height="170px"></ion-img>
<div class="card-title">{{ (nextEvent$ | async)?.title }}</div>
<div class="card-subtitle">{{ (nextEvent$ | async)?.timestampStarts | date:'fullDate' }} - {{ (nextEvent$ | async)?.timestampStarts | date:'HH:mm' }}</div>
</div>
<ion-item>
<img class="sport-icon" src="sports-icons/icon-{{(nextEvent$ | async)?.sport}}.png" item-left>
<h2>{{(nextEvent$ | async)?.title}}</h2>
<p>{{(nextEvent$ | async)?.sport | hashtag}}</p>
</ion-item>
<ion-item>
<ion-icon name="navigate" isActive="false" item-left small></ion-icon>
<h3>{{(nextEvent$ | async)?.location.main_text}}</h3>
<h3>{{(nextEvent$ | async)?.location.secondary_text}}</h3>
</ion-item>
<ion-item>
<ion-icon name="time" isActive="false" item-left small></ion-icon>
<h3>{{(nextEvent$ | async)?.timestampStarts | date:'HH:mm'}} - {{(nextEvent$ | async)?.timestampEnds | date:'HH:mm'}}</h3>
</ion-item>
</ion-card>
RXJS 的 concatMap
方法将所有事件扁平化为一个可观察对象。您最好使用 switchMap
方法。 switchMap 仅订阅最新的可观察对象。
所以这样做可能会解决您的问题:
之前:
return this.af.list(path, { query: query }).concatMap(event => this.getEvent(event[0].id));
之后:
return this.af.list(path, { query: query }).switchMap(event => event);
this.af.list(path, { query: query }).concatMap(event => this.getEvent(event[0].id))
是感冒Observable
。这意味着每次你对其执行订阅时,它都会重新执行底层流,也就是重新调用getEvent
方法。
async
隐式订阅 Observable
,这就是为什么如果您在模板中计算 (nextEvent$ | async)
调用,您将看到 11 来自哪里。
&tldr; 您需要分享对流的订阅:
this.nextEvent$ = this.eventService.getNextEventForUser(this.cuid)
// This shares the underlying subscription between subscribers
.share();
以上将在第一次订阅时连接流,但随后将在所有订阅者之间共享该订阅。