首页 > 解决方案 > 如何在不丢失活跃订阅者的情况下更新 Observable?

问题描述

我目前在尝试使用publishReplayand更新缓存值时遇到了一些麻烦refCount

首先,有问题的方法

getCurrentUser(): Observable<User> {
    if (!this.jwtService.getToken() || this.jwtService.getAnonymousToken()) { return of(null); }
    if (!this.currentUser$) {
      console.warn('user.service.getcurrentuser -> load and CACHE it once');
      this.currentUser$ = this.userService.loadCurrentUser().pipe(
        // https://medium.com/angular-in-depth/fastest-way-to-cache-for-lazy-developers-angular-with-rxjs-444a198ed6a6
        publishReplay(1),
        refCount()
      );
    }
    console.log('user.service.getcurrentuser -> get from CACHE');
    return this.currentUser$;
 }

this.currentUser$是一个 Observable 类型User

该方法本身按预期工作,该方法的第一次调用从服务器加载当前用户。随后的调用将获取缓存的条目。但是,我现在要做的是删除缓存的值并从服务器重新加载用户(即在前端编辑了某些内容并且用户数据需要重新加载)。

我做到了

removeCachedUser(): void {
  this.currentUser$ = null;
}

通过这样做,方法中的if条件getCurrentUser被触发,用户从服务器重新加载。到目前为止,一切都很好。

我面临的问题是getCurrentUser在缓存重置之前订阅的方法没有收到任何更新,即

ngOnInit() {
  this.currentUser$ = this.authService.getCurrentUser();
}

我怀疑这是因为我覆盖了this.currentUser$Observable。

this.currentUser$ = this.userService.loadCurrentUser()

这篇文章或多或少地证实了我的理论。

我对吗?如果是这样,处理这个问题的正确方法是什么?不幸的是,我没有反应式后端,所以我必须手动触发重新加载。提前致谢!

标签: angulartypescriptrxjs

解决方案


您必须创建一个可以多次发出的 Observable 才能向活动订阅者发送新值。在您当前的设置this.currentUser$完成时this.userService.loadCurrentUser()完成。我怀疑loadCurrentUser是一个发出一次并完成的http请求。所以this.currentUser$不能多次发射。

要创建可以多次发出的 Observable,请使用 aSubject作为源并映射到您的 http 请求。要让用户在第一次订阅currentUser$之前reloadUser()被调用,这Subject应该是一个BehaviorSubject.

private _reloadUser = new BehaviorSubject<void>(0 as void);

currentUser$ = this._reloadUser.pipe(
  switchMap(() => {
    if (!this.jwtService.getToken() || this.jwtService.getAnonymousToken()) { 
      return of(null); 
    }
    console.warn('user.service.getcurrentuser -> load and CACHE it');
    return this.userService.loadCurrentUser();
  }),
  shareReplay(1),
  tap(user => console.log('user from CACHE:', user))
) 

reloadUser() {
  this._reloadUser.next();
}

switchMap调用 to将reloadUser()取消任何正在进行的加载请求并发送新的加载请求。如果当前正在加载用户,您也可以使用if 调用不应该重新加载用户来exhaustMap代替。switchMapreloadUser()

shareReplay(1)publishReplay(1), refCount()在这种情况下更适合,因为默认情况下它没有该refCount功能。如果没有任何订阅者,publishReplay(1), refCount()新订阅者将触发加载请求。在这种情况下,将改为从“缓存”返回当前用户。currentUser$shareReplay(1)


推荐阅读