[英]Initialize cache when it is used
可以说我发生以下事件:
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
并避免error
和complete
因为这些将停止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.