繁体   English   中英

使用时初始化缓存

[英]Initialize cache when it is used

可以说我发生以下事件:

  • 做一点事
  • FetchSomething
  • FetchSomethingSuccessful

DoSomething所做的事情需要一些缓存的数据。 当我触发该事件时,我想查看缓存并对其进行处理(如果存在)。 如果不是,那么我想获取它,等待它在缓存中,然后重试。

我想出了以下解决方案,但感觉就像我在滥用map运营商。 是否有更合适的运算符可用来观察流并抛出错误或达到重试的效果?

 const DO_SOMETHING = 'DO_SOMETHING'; const FETCH_SOMETHING = 'FETCH_SOMETHING'; const FETCH_SOMETHING_SUCCESSFUL = 'FETCH_SOMETHING_SUCCESSFUL'; const events = new Rx.Subject(); const cache = new Rx.BehaviorSubject(null); // the magic sauce const cacheInitializer = cache // is there a better way than this? .map(x => { if (!x) { throw 'empty'; } return x; }) .retryWhen(x => { console.log('Cache is empty'); events.next(FETCH_SOMETHING); return events.filter(x => x === FETCH_SOMETHING_SUCCESSFUL) .first(); }); // fake getting the data events.filter(x => x === FETCH_SOMETHING) .do(() => { console.log('Fetching data'); }) .delay(1000) .subscribe(x => { console.log('Data fetched and put into cache'); cache.next({ data: 1 }); events.next(FETCH_SOMETHING_SUCCESSFUL); }); // handle doing something events.filter(x => x === DO_SOMETHING) .do(() => { console.log('Check the cache'); }) .switchMapTo(cacheInitializer) .subscribe(x => { console.log('Do something', x); }); events.next(DO_SOMETHING); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script> 

上下文:我正在使用ngrx和效果。 FETCH_SOMETHING将触发另一个效果,而FETCH_SOMETHING_SUCCESSFUL将指示效果已成功完成。 在这种情况下,我的缓存是ngrx。 我不认为我想在API层中进行缓存,因为这仍然会使我从缓存的响应中更新状态,而不仅仅是依赖状态中的数据。

您需要一个简单的shareReplay ,但是您需要rxjs 5.5.0或更高版本,因为已经修复了一个错误。

const cached$ = request$.shareReplay(1);

进行第一个订阅时,这将触发请求,但后续订阅将使用缓存的值。

将错误传递给订阅者,然后销毁内部主题,以便不缓存错误本身。 这使得可观察的重试。 因此,您可以附加所需的任何重试逻辑(例如重试直到成功)。

最后,如果refCount在某个时候变为0,则缓存也将持续存在。

shareReplay还需要第二个参数,就像ReplaySubject构造函数一样,它定义一个保留缓存的时间窗口。

 // Faked request which sometimes errors const request$ = Rx.Observable .defer(() => Rx.Observable.of(Math.random())) .do(() => console.log('API called')) .map(val => { if (val <= 0.3) { console.log('API error'); throw val; } else { return val; } }) .delay(250); const cached$ = request$.shareReplay(1); Rx.Observable.timer(0, 1000) .take(5) .switchMap(() => cached$.retry(5)) .subscribe(console.log); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script> 

在缓存HTTP响应时,我使用类似以下的内容:

let counter = 1;
const cache$ = new ReplaySubject(1, 1000);

// Mock HTTP request
const http$ = Observable
  .defer(() => Observable.of(counter++).delay(100).do(val => cache$.next(val)))
  .share();

const source$ = Observable
  .merge(cache$, http$)
  .take(1);

观看现场演示: https : //jsbin.com/rogetem/9/edit?js,控制台

它仍然需要一个额外的变量cache$ 我相信没有它也可以实现,但是我想我必须使用materialize()dematerialize() ,这很令人困惑。

您知道调用defer()的回调时缓存为空。 现在它只是增加counter

请注意,我仍然必须使用.do(val => cache$.next(val))仅将next通知传递给ReplaySubject并避免errorcomplete因为这些将停止Subject。

您可以看到它确实适用于以下示例:

source$.subscribe(val => console.log("Response 0:", val));
setTimeout(() => source$.subscribe(val => console.log("Response 50:", val)), 50);
setTimeout(() => source$.subscribe(val => console.log("Response 60:", val)), 60);
setTimeout(() => source$.subscribe(val => console.log("Response 200:", val)), 200);
setTimeout(() => source$.subscribe(val => console.log("Response 1200:", val)), 1200);
setTimeout(() => source$.subscribe(val => console.log("Response 1500:", val)), 1500);
setTimeout(() => source$.subscribe(val => console.log("Response 3500:", val)), 3500);

这将输出以下输出(每个值被高速缓存1秒钟):

Response 0: 1
Response 50: 1
Response 60: 1
Response 200: 1
Response 1200: 2
Response 1500: 2
Response 3500: 3

暂无
暂无

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

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