2017-12-13 24 views
0

私はAngular 4アプリケーションを持っています。これはシステムのダッシュボードとして機能します。 複数の異なるコンポーネントが、同じTypeScriptサービスクラスを使用して同じバッキングREST呼び出しを呼び出します。これがうまくいく間に、クライアント側にキャッシュサービスを導入することで、サーバを叩く不必要な重複リクエストストームを回避したいと思います。折り返し角4観察可能なHTTP呼び出しを1つにキャッシングするためにObservable

私は、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. キャッシュ戻り値
  4. 各サブスクライバに値を返し、のように、HTTP Observableのように自分自身をクリーンアップします。

どうすればよいですか?

挑戦は

  • サブスクリプションは、ラップcomputeFunction戻る前に作られたときObservableは、両方のケースを扱うべきであるということです。 (ラップされたObservableがサブスクリプションを呼び出すまで待ち​​ます)、
  • ラップされたサブスクリプションは返されます。computeFunctionが返されます。 (キャッシュされた値を提供)。

間違った方向に向かい、全体が複雑すぎるのですか?私が従うことができるより単純な概念があれば、それを学ぶことにもっと感謝します。

+0

あなたは 'share'd observableやpromiseを値としてではなく保存してください。 – estus

+0

私はあなたの現在の実装がかなり標準的だと思うし、あなたはaysncも待っている完璧なキャッシング機構を望んでいると思う。 Observableのようなストアを使用してキーと値を格納することができます。値はobservable.of(値)またはayncコールobservable(例:fetch(..)。fromPromise値=> ...)、しかし、私は詳細の実装がはるかに難しいと思う。 IMHOそれは価値がありません –

+0

あなたが受け取った回答にフィードバックを提供し、あなたの質問に答えた場合は受け入れたものとしてマークしてください。 –

答えて

1

多くのファンシーロジックは必要ありません。あなたはobservableをマルチキャストするために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()を取り除くことができunsubscribe - メモリリークに注意してください。小さなノートで


Observable.never()トリックは唯一this fixed bugによるrxjsの非常に最近のバージョンの上で動作します。同じことがタイマーベースのソリューションにも当てはまります。

関連する問題