在函数中返回可观察管道与分配给变量

And*_*hov 2 javascript class rxjs firebase angular

我有一个功能代码片段,它使用 Angular 服务创建 Observable 管道。该代码使用 mergeMap、filter、map 和 shareReplay(1) 运算符来处理用户身份验证并从用户配置文件中检索加入状态。该管道旨在重复使用,以有效地观察入职状态的变化。

当我将此逻辑封装在函数中并返回生成的 Observable 或将其分配给类属性时,我观察到不同的行为。当我从函数返回 Observable 时,管道内的整个链在每个订阅上执行。另一方面,如果我将 Observable 分配给类属性,则 shareReplay(1) 运算符将按预期工作,并且整个链仅执行一次。

我对这种差异感到困惑,并怀疑这可能与新实例的创建有关。你能帮我理解为什么从函数返回 Observable 和将其分配给类属性之间的行为不同吗?如何在这两种情况下使用 shareReplay(1) 运算符实现一致的行为?

user$ = user(this.auth).pipe(shareReplay(1));

getStatus(): Observable<> {
 return this.angularService.user$.pipe(
            mergeMap(user => {
                console.log('Onboarding merge map');
                return this.angularService.getUserProfile(user?.uid || "").pipe(
                    filter(({ profile, user }) => {
                        console.log('Filter: ', profile['onboarding'])
                        return profile && profile['onboarding'];
                    }),
                    map(d => d.profile['onboarding'])
                )
            }),
            shareReplay(1)
        );
}
Run Code Online (Sandbox Code Playgroud)
user$ = user(this.auth).pipe(shareReplay(1));

readonly onboardingStatus$: Observable<> = this.angularService.user$.pipe(
            mergeMap(user => {
                console.log('Onboarding merge map');
                return this.angularService.getUserProfile(user?.uid || "").pipe(
                    filter(({ profile, user }) => {
                        console.log('Filter: ', profile['onboarding'])
                        return profile && profile['onboarding'];
                    }),
                    map(d => d.profile['onboarding'])
                )
            }),
            shareReplay(1)
        );
Run Code Online (Sandbox Code Playgroud)

更新:添加可能导致在返回时调用“new”的函数,如果将其更改为变量可能会改变原始函数的行为,则会给出更新:

getUserProfile(uid: string): Observable<{ profile: DocumentData, user: User }> {
        return documentSnapshot(doc(this.firestore, "users", uid)).pipe(
            tap((val) => console.log(`userProfile document snapshot executed`, val)),
            mergeMap((document) => (of({ profile: document.data(), user } as any))),
            shareReplay(1)
        );
    }
Run Code Online (Sandbox Code Playgroud)

Pic*_*cci 5

如果你有这样的功能

function retObs() {
   return of('something')
}
Run Code Online (Sandbox Code Playgroud)

任何时候你调用这样的函数,你都会得到一个新的 Observable 实例。因此,如果你有这样的东西

function retObs() {
   return of('something'),pipe(share(1))
}
const sub1 = retObs().subscribe()
const sub2 = retObs().subscribe()
Run Code Online (Sandbox Code Playgroud)

每个订阅都订阅不同的 Observable 链,因此shareReplay(1)对这些订阅没有任何影响。

相反,如果将返回的 Observable 实例保存在变量中,则该变量持有的 Observable 的所有订阅共享同一个 Observable 链,因此您会看到预期retObs()的效果。shareReplay(1)

const var = retObs()
const sub1 = var.subscribe()
const sub2 = var.subscribe()
Run Code Online (Sandbox Code Playgroud)

这个stackblitz展示了这个概念的实际应用。

  • 任何时候执行 `this.angularService.user$.pipe(....)` 都会创建一个新的 Observable。这与在数组上调用“slice”是一样的,它返回一个新数组。任何时候你调用 `getStatus()` 都会创建一个新的 Observable 实例。在创建此类实例的过程中,您还调用了“getUserProfile”,但我想说这是一个细节,而不是根本原因。 (2认同)