嵌套的Angular/RxJs switchMap和Angularfire firebase docData调用两次而不是一次

y53ybaqx  于 2023-11-21  发布在  Angular
关注(0)|答案(1)|浏览(166)

我使用RxJS在Angular中有一个复杂的可观察链,我遇到了嵌套的switchMap和docData调用的意外行为。可观察链涉及使用docData使用额外的用户数据丰富从this.stream_service.readFeed()获取的活动。然而,我遇到了docData调用的意外行为,特别是tapswitchMap运算符。
我预计docData next和combineLatest next日志只出现一次,因为activity_response数组中只有一个项。
尽管有可观察到的发射值,但tap和switchMap操作符的日志记录次数比预期的要多。docData next和combineLatest next日志出现两次而不是一次。
代码如下:

private post_activity$ = toObservable(this.post).pipe(
    // Use switchMap to switch to a new observable based on the latest value of post$
    switchMap((post) => {
      // Check if post is falsy (null, undefined, etc.)
      if (!post) {
        // If post is falsy, return an EMPTY observable (essentially a complete and empty observable)
        return EMPTY;
      }

      // Call the current_user() function to get the current user
      const current_user = this.current_user();

      // Determine the feed_type based on whether the current user matches the post's user_id
      const feed_type =
        current_user && current_user?.uid === post.user_id ? 'user' : 'public';

      // Return the result of calling this.stream_service.readFeed()
      return this.stream_service.readFeed(
        feed_type,
        post.user_id,
        1,
        post.stream_activity_id
      );
    }),
    tap((post_activity) => {
      console.log('%cPost Activity', 'color: pink', post_activity);
    }),
    switchMap((activity_response) => {
      // Check if activity_response is falsy
      if (!activity_response) {
        // If it is, return an EMPTY observable
        return EMPTY;
      }
      // Call a service method to enrich the activities in activity_response
      if (activity_response.length > 0) {
        const activity = activity_response[0]; // 1 activity
        console.log('Activity', activity); // This will log once
        return docData(doc(this.firestore, 'users', activity.actor), {
          idField: 'id',
        }).pipe(
          tap({
            next: (user) => console.log('docData next', user),
            error: (error) => console.log('docData error', error),
            complete: () => console.log('docData complete'),
          }), // this will log twice
          switchMap((user: any) => {
            const activity_user = {
              username: user.username,
              pronouns: user.pronouns,
              avatar_url: user.avatar_url,
              photo_url: user.photo_url,
            };
            return of({
              user: activity_user,
              activity: activity,
              liked:
                !!activity.own_reactions?.like &&
                activity.own_reactions.like[0].user_id ===
                  this.user_service.current_user()?.uid,
            });
          }),
          tap({
            next: (activities) => console.log('combineLatest next', activities),
            error: (error) => console.log('combineLatest error', error),
            complete: () => console.log('combineLatest complete'),
          }) // this will log twice too
        );
      } else {
        return EMPTY;
      }
    }),
    tap(() => {
      this.count.set(this.count() + 1);
      console.log('%cCount', 'color: lime', this.count()); // Count 2
    })
  );

字符串
什么可能导致可观察链中的意外行为,以及如何确保docData next和combineLatest next日志按预期只出现一次?
任何关于调试和解决此问题的见解或指导将不胜感激。谢谢!
附加信息:
我添加了一些代码片段来检查隔离它是否会记录docData()两次。然而,我最终让事情变得更加令人困惑,因为我发现不仅docData()只记录一次(正如预期的那样),而且我在switchMap中的代码也只记录一次。这真是令人难以置信!

data$ = docData(
    doc(this.firestore, 'users', activity.actor)
  ).pipe(
    tap({
      next: (user) => console.log('docData next', user),
      error: (error) => console.log('docData error', error),
      complete: () => console.log('docData complete'),
    }) // this will log twice
  );
  private readonly data = toSignal(this.data$);

0yg35tkg

0yg35tkg1#

从你所展示的来看,唯一的原因可能是docdata发出了多个值。这是tap可以记录两次的唯一方式。如果每次的用户都是相同的,那么docdata可能出于某种原因返回了两次。
尝试将管道更改为:

docData(...).pipe(
  tap(... log stuff...), // should print twice
  distinctUntilChangedKey("username"), // filter out duplicates
  tap(... log stuff ...), // does this print once now?
)

字符串
评价结果如下:
如果第一次点击打印2x,第二次点击打印1x,那么这意味着docData两次发出相同的用户名。
如果两个点击都打印了2x,那么docData会发出2个 * 不同的 * 用户(不同的用户名,但是查询的id相同)。
请注意,这两种情况都表明问题在docData中,但它们应该有助于缩小原因范围。
编辑:我不熟悉toSignal,但它很有可能是重复数据删除(有效地使用distinctUntilChanged),这就是为什么你的代码版本打印了两次tap,但订阅信号的东西只被调用一次。

相关问题