简体   繁体   English

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

[英]RxJS (and Angular): Attempting to cache one-time HTTP requests still causes multiple requests

I'm still in the process of getting comfortable with RxJS, so this is potentially an easy question. 我仍在适应RxJS,因此这可能是一个简单的问题。

Currently I am attempting to lazily make an XHR request for some data that I only need to fetch once and then cache indefinitely while the page is open, and I think I'm on the right track by trying to leverage an AsyncSubject with the value emitted from Angular's HTTP client. 目前,我正在尝试对某些我只需要获取一次的数据进行XHR延迟请求,然后在打开页面时无限期地对其进行缓存,并且我认为通过尝试使用带有所发出的值的AsyncSubject ,我处于正确的轨道来自Angular的HTTP客户端。 What I have so far basically looks like this: 到目前为止,我基本上拥有的是这样的:

@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) { }
     ...
}

This almost works since the request isn't made until something subscribes to extraInfo$ , and .share() should prevent additional requests being made when I have more than 1 observer on it. 这几乎是extraInfo$ ,因为直到有人订阅了extraInfo$才发出请求,并且当我有多个观察者时, .share()应该可以防止发出其他请求。

However, if I unsubscribe to it and extraInfo$ becomes cold (since there are 0 subscribers), subscribing to it again causes an additional request to be made again. 但是,如果我取消订阅,并且extraInfo$变冷(因为有0个订阅者),则再次订阅它会导致再次发出附加请求。

Right now I'm tempted to override the ._subscribe() property on an AsyncSubject so that I can run the request when it gets its first observer, but that feels a bit too hackish. 现在我很想覆盖._subscribe()上的属性AsyncSubject ,这样,当它得到了第一观察者,我可以运行的要求,但感觉有点太hackish的。

If you want to perform a request and then cache the result for this on any subsequent subscription, you can do this a lot easier: 如果要执行请求,然后将其结果缓存在任何后续订阅中,则可以更轻松地完成此操作:

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

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

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

By using shareReplay(1) you are fixing the problem you had with the share operator. 通过使用shareReplay(1)您可以解决share运算符遇到的问题。 They are both multicasting operators but with different properties. 它们都是多播运营商,但具有不同的属性。 You want the operator to be repeatable and not retryable (checkout this article I wrote on the subject to help you http://blog.kwintenp.com/multicasting-operators-in-rxjs/ understanding what I mean). 您希望操作员是可重复的,而不是可重试的(请查看我在该主题上写的这篇文章,以帮助您http://blog.kwintenp.com/multicasting-operators-in-rxjs/了解我的意思)。

Just remember, If you want to cache a certain observables result indefinitely, shareReplay is the one you need. 只要记住,如果您想无限期地缓存某些可观察的结果, shareReplay是您需要的结果。

I came up with something eventually, the following works for what I need: 我最终想出了一些办法,以下是我需要的工作:

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) { }
   ...
}

I decided to use a BehaviorSubject instead of AsyncSubject since I can expand to set intervals on refreshing the data from here if I need to. 我决定使用BehaviorSubject而不是AsyncSubject因为如果需要,我可以扩展以设置刷新间隔的时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM