RxJS(和Angular):尝试缓存一次性HTTP请求仍然会导致多个请求

时间:2017-10-05 02:32:46

标签: javascript angular typescript rxjs

我仍然在使用RxJS,所以这可能是一个简单的问题。

目前我试图懒洋洋地对某些数据做出XHR请求,我只需要获取一次,然后在页面打开时无限期地缓存,我想我试图利用{{{} { 1}}使用Angular的HTTP客户端发出的值。到目前为止我基本上看起来像这样:

AsyncSubject

这几乎可以使用,因为在订阅@Injectable() class AuthService { user$ = new BehaviorSubject(null); // do .switchMap() so we reset when the auth'ed user changes extraInfo$ = this.user$.switchMap(() => { return this.http.get('/api/account').share(); }); constructor(http: HttpClient) { } ... } 之前不会发出请求,extraInfo$应该阻止在我有超过1个观察者时发出其他请求。

但是,如果我取消订阅并且.share()变冷(因为有0个订阅者),再次订阅它会导致再次发出请求。

现在我很想覆盖extraInfo$上的._subscribe()属性,这样我就可以在它获得第一个观察者时运行请求,但这感觉有些过于hackish。

2 个答案:

答案 0 :(得分:1)

如果您想要执行请求,然后在任何后续订阅中缓存此结果,您可以更轻松地执行此操作:

@Injectable()
class AuthService {
     user$ = new BehaviorSubject(null);

     extraInfo$ = this.user$.switchMap(() => {
         return this.http.get('/api/account').shareReplay(1);
     });

     constructor(http: HttpClient) { }
     ...
}

使用shareReplay(1)解决了share运营商遇到的问题。它们都是多播运营商,但具有不同的属性。你希望操作符是可重复的而不是可重试的(查看我在这个主题上写的这篇文章,以帮助你http://blog.kwintenp.com/multicasting-operators-in-rxjs/理解我的意思)。

请记住,如果您想无限期地缓存某个可观察的结果,shareReplay就是您需要的结果。

答案 1 :(得分:0)

我最终想出了一些东西,以下是我需要的东西:

const lastFetchedInfo = new WeakMap();

@Injectable()
class AuthService {
   user$ = new BehaviorSubject(null);
   // do .switchMap() so we reset when the auth'ed user changes
   extraInfo$ = this.user$.switchMap(user => !user ? Observable.empty() : new Observable((observer) => {
     if (!lastFetchedInfo.has(user)) {
       const subject = new BehaviorSubject(null);
       lastFetchedInfo.set(user, subject.filter(o => o !== null));
       this.http.get('/api/account').subscribe(info => subject.next(info));
     }
     const subscription = lastFetchedInfo.get(user);
     return () => subscription.unsubscribe();
   }));

   constructor(http: HttpClient) { }
   ...
}

我决定使用BehaviorSubject代替AsyncSubject,因为如果需要,我可以扩展以设置刷新数据的时间间隔。