【问题标题】:Duplicate calls when using concatMap使用 concatMap 时重复调用
【发布时间】:2017-05-31 19:21:48
【问题描述】:

我想在视图中显示用户注册的下一个即将发生的事件。

为此,我需要首先检索用户注册的最近事件(及时),然后检索该事件的信息。

用户注册的事件列表是动态的,我需要连续使用两个 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>

【问题讨论】:

    标签: angular rxjs observable angular2-observables concatmap


    【解决方案1】:

    this.af.list(path, { query: query }).concatMap(event =&gt; 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();
    

    上面将在第一次订阅时连接流,但随后将在所有订阅者之间共享该订阅。

    【讨论】:

    • 谢谢保罗丹尼尔斯!我实际上注意到了 11 个异步管道,但担心这只是巧合。这些冷的 Observable 的目的是什么? switchMap 会避免共享订阅吗?我想暂时避免执行过于复杂的实现。
    • @ManuelRODRIGUEZ no switchMap 不会解决问题。冷Observables 的想法是功能纯度,本质上是每次您使用一组参数执行操作时,您应该期望收到相同的答案。显然,在现实世界中,这并不总是现实的,因此为什么存在像 share 这样的运算符,将冷的 Observables 转换为热的,就像这样的场景。
    【解决方案2】:

    RXJS 的 concatMap 方法将所有事件扁平化为一个 observable。使用switchMap 方法可能会更好。 switchMap 只订阅最近的 observable。

    所以做这样的事情可能会解决你的问题:

    之前:

    return this.af.list(path, { query: query }).concatMap(event =&gt; this.getEvent(event[0].id));

    之后:

    return this.af.list(path, { query: query }).switchMap(event =&gt; event);

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-04
      • 1970-01-01
      相关资源
      最近更新 更多