问题
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable()
export class ApiService {
constructor(private http: HttpClient) { }
// get API request
public apiGetRequest(url: any): Observable<any> {
return this.http.get(url)
.pipe(
catchError(this.handleError('apiGetRequest'))
);
}
}
I am using angular 5 with rxjs version 5.5.6 I am trying to cache multiple http get request.
回答1:
Try something like this:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
@Injectable()
export class ApiService {
private _someData: any;
constructor(private http: HttpClient) { }
public apiGetRequest(url: any): Observable<any> {
/**
* Returns cached data if exists.
*/
if (this._someData) {
return of(this._someData);
}
return this.http.get(url)
.pipe(
tap((someData: any) => {
if (someData) {
this._someData = someData;
}
}),
catchError(this.handleError('apiGetRequest'))
);
}
}
In first time we fetch data from backend api, next times apiGetRequest()
will be returns cached data.
回答2:
this way without rxjs
🧙♂️ but you can check this answer
@Injectable()
export class CashingService {
private __cach = {}
isCashed(url: string) {
return this.__cach[url];
}
getData(url: string) {
return this.__cach[url]
}
setData(url) {
return (data) => {
if (data && (data instanceof Error) === false)
this.__cach[url] = data
};
}
reset() {
this.__cach = {};
}
}
inject chaching service
@Injectable()
export class ApiService {
constructor(private http: HttpClient , private _c:CachingService ) { }
// get API request
public apiGetRequest(url: any): Observable<any> {
if (this._c.isCashed(url)){
return of(this._c.getData(url));
} else {
return this.http.get(url)
.pipe(
tab(this._c.setData(url)),
catchError(this.handleError('apiGetRequest'))
);
}
}
}
回答3:
The right way to do it would be like this:
private varName$: Observable<T>;
private _apiGetRequest():Observable<T>{
return this.http.get('endpoint');
}
public apiGetRequest():Observable<T>{
if(!this.varName$){
this.varName$ = this._apiGetRequest().pipe(shareReplay(1));
}
return this.varName$
}
This way, the private observable keeps the last emitted value
回答4:
This is cache service I'm using, it stores data in Redux store, but it's ease to change to any data holder you want.
@Injectable()
export class CacheManager {
constructor(
private store: NgRedux<IAppState>,
private actions: GlobalActions) {
}
public get<T>(key: CacheKey, thisArg: any, dataRetriver: (...args: any[]) => T | Observable<T>, ...args: any[]): Observable<T> {
if (this.isCached(key)) {
return of(this.getFromCache<T>(key));
}
const data$: T | Observable<T> = dataRetriver.apply(thisArg, args);
if (data$ instanceof Observable) {
return data$.pipe(
tap(result => this.addToCache(key, result))
);
}
else {
this.addToCache(key, data$);
return of(data$);
}
}
public clear(): void {
this.store.dispatch(this.actions.clearCache());
}
private isCached(key: string): boolean {
return this.cacheStorage[key] !== undefined;
}
private addToCache<T>(key: string, value: T): void {
this.store.dispatch(this.actions.cache(key, value));
}
private getFromCache<T>(key: string): T {
return <T>this.cacheStorage[key];
}
private get cacheStorage(): { [key: string]: any } {
return this.store.getState().global.cache;
}
}
and example to use:
// call without cache:
this.service.loadPeriods(param1, param2)
.pipe(...)
.subscribe(...)
// call with cache ("periods" is a cache key):
this.cache.get("periods", this.service, this.service.loadPeriods, param1, param2)
.pipe(...)
.subscribe(...);
回答5:
You can create internal key/value pair storage solution to cache the request. Here I am using Map() for this.
import { Injectable } from '@angular/core';
import { HttpEvent, HttpRequest, HttpHandler, HttpInterceptor, HttpResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { tap, shareReplay } from 'rxjs/operators';
@Injectable()
export class CacheInterceptor implements HttpInterceptor {
private cache = new Map<string, any>();
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (request.method !== 'GET') {
return next.handle(request);
}
const cachedResponse = this.cache.get(request.url);
if (cachedResponse) {
return of(cachedResponse);
}
return next.handle(request).pipe(
tap(event => {
if (event instanceof HttpResponse) {
this.cache.set(request.url, event);
}
})
);
}
}
Now You need to take care of cache invalidation. whenever data changes you need to invalidate.
来源:https://stackoverflow.com/questions/55136593/how-to-perform-caching-http-get-request-in-angular-5