2017-12-13 11 views
0

시스템의 대시 보드 역할을하는 Angular 4 애플리케이션이 있습니다. 여러 구성 요소가 동일한 TypeScript 서비스 클래스를 통해 동일한 백업 REST 호출을 호출합니다. 이 작업을하는 동안 클라이언트 측에 캐싱 서비스를 도입하여 서버를 망치는 불필요한 중복 요청을 피하고 싶습니다.감싸는 각도 4 관찰 가능한 HTTP 호출을 캐싱하기 위해 관찰 가능

나는 다음 HTTP가 computeFunction로 전화를 전달할 내 서비스에 의해 사용된다 (타이프에서) 내 캐시,이 같은 것을 구현 :

@Injectable() 
export class CacheService { 

    private cacheMap = {}; 


    getAsObservable<V>(
       key: string, 
       expirationThresholdSeconds: number, 
       computeFunction:() => Observable<V>): Observable<V> { 

    const cacheEntry = this.cacheMap[key]; 

    if (...) { 
     // if cached entry is valid, return it immediately 

     return Observable.of<V>(cacheEntry.value);   
    } else { 
     // if not found or expired, call the method, and use map(...) to store the returned value 
     return computeFunction().map(returnValue => { 

     const expirationTime = new Date().getTime() + (expirationThresholdSeconds * 1000); 

     const newCacheEntry = ... // build cache entry with expiration set 

     this.cacheMap[key] = newCacheEntry; 

     return returnValue; 
    }); 
    } 

} 

이 호출하는 경우, 그러나, 제대로 작동 동일한 key이 빠르게 연속적으로 만들어지면 (예 : 응용 프로그램이 시작될 때) 캐시가 확인시 리턴 값을 갖지 않으므로 서버에 대해 모두 해고됩니다.

그래서 나는

  1. 한 번만 computeFunction에 전달 된 호출을 실행하는 것이, 내가 대신 어떻게 든 여러 캐시 호출자에게 반환 할 수 있습니다 내 자신의 캐시 래퍼 "멀티플렉싱"Observable를 구현해야한다고 생각
  2. 캐시합니다 반환 값
  3. 값을 각 구독자에게 반환 한 다음 이 없도록 HTTP Observable처럼 자신을 정리합니다.

누군가이 방법에 대한 샘플을 제공해 주시겠습니까?

문제는

  • 구독은, 랩 computeFunction 돌아 가기 전에 만든 때 Observable이 두 경우 모두를 처리해야한다는 것입니다. (포장 된 Observable이 구독을 호출 할 때까지 대기) 및
  • 포장 된 후 구독을 반환했습니다. computeFunction. (캐시 된 값 제공).

아니면 잘못된 방향으로 들어가서 모든 것을 복잡하게 만드는가? 따라 할 수있는 더 간단한 개념이 있다면, 나는 그것을 배우는 것에 더 감사 할 것입니다.

+0

'share'd observable 또는 promise가 아닌 값을 저장해야합니다. – estus

+0

당신의 현재 구현은 꽤 표준적인 것으로 생각하고 aysnc를 기다리는 완벽한 캐싱 메커니즘을 원한다고 생각합니다. Observable과 같은 저장소를 사용하여 키와 값을 저장할 수있는 곳은 어디에도 없습니다. value는 observable.of (value) 또는 aync call observable (예 : fetch (..). fromPromise 값 => ...),하지만 세부 구현하는 것이 훨씬 어렵다고 생각합니다. IMHO 가치가 없습니다. –

+0

받은 답변에 피드백을 제공하고 질문에 답변 한 경우이를 수락 한 것으로 표시하십시오. –

답변

1

멋진 로직이 많이 필요하지 않습니다. 관찰 가능 항목을 멀티 캐스트하려면 shareReplay(1)을 사용하면됩니다.

// Simulate an async call 
// Side effect logging to track when our API call is actually made 
const api$ = Observable.of('Hello, World!').delay(1000) 
    .do(() => console.log('API called!')); 

const source$ = api$ 
    // We have to make sure that the observable doesn't complete, otherwise 
    // shareReplay() will reconnect if the ref count goes to zero 
    // in the mean time. You can leave this out if you do actually 
    // want to "invalidate the cache" if at some point all observers 
    // have unsubscribed. 
    .concat(Observable.never()) 
    // Let the magic happen! 
    .shareReplay(1); 

지금 당신이 모든 당신이 원하는 구독 할 수 있습니다 :

// Two parallel subscriptions 
const sub1 = source$.subscribe(); 
const sub2 = source$.subscribe(); 

// A new subscription when ref count is > 0 
sub1.unsubscribe(); 
const sub3 = source$.subscribe(); 

// A new subscription after ref count went to 0 
sub2.unsubscribe(); 
sub3.unsubscribe(); 
const sub4 = source$.subscribe(); 
sub4.unsubscribe(); 

을 그리고 당신은 볼 수 모두 하나의 로그 성명 다음은 그 예입니다. 이 모든 관찰자 때까지 API를 쿼리합니다 뜨거운 스트림이라고하지만

const source$ = Observable.timer(0, expirationTimeout) 
    .switchMap(() => api$) 
    .shareReplay(1); 

참고 : 당신은 시간 기반 만료를 원하는 경우

, 당신은이 작업을 수행하는 대신 never()을 없애 수 있습니다 구독 취소 - 메모리 누수에주의하십시오.작은 노트에


Observable.never() 트릭은 this fixed bug 때문에 rxjs의 매우 최신 버전에서 작동합니다. 타이머 기반 솔루션에서도 마찬가지입니다.