我想用RxJs实现Time Expiry缓存。这是"普通"的例子。缓存:
//let this represents "heavy duty job"
var data = Rx.Observable.return(Math.random() * 1000).delay(2000);
//and we want to cache result
var cachedData = new Rx.AsyncSubject();
data.subscribe(cachedData);
cachedData.subscribe(function(data){
//after 2 seconds, result is here and data is cached
//next subscribe returns immediately data
cachedData.subscribe(function(data2){ /*this is "instant"*/ });
});
第一次调用subscribe
cachedData
时,"重负荷工作"被调用,2秒后结果保存在cachedData
(AsyncSubject
)中。 subscribe
上的任何其他后续cachedData
会立即返回已保存的结果(因此缓存实现)。
我想要达到的目标是" spice" cachedData
以内的时间段有效,当该时间过去时,我想重新运行"重型工作"对于新数据并在新时间段再次缓存此等...
期望的行为:
//pseudo code
cachedData.youShouldExpireInXSeconds(10);
//let's assume that all code is sequential from here
//this is 1.st run
cachedData.subscribe(function (data) {
//this first subscription actually runs "heavy duty job", and
//after 2 seconds first result data is here
});
//this is 2.nd run, just after 1.st run finished
cachedData.subscribe(function (data) {
//this result is cached
});
//15 seconds later
// cacheData should expired
cachedData.subscribe(function (data) {
//i'm expecting same behaviour as it was 1.st run:
// - this runs new "heavy duty job"
// - and after 2 seconds we got new data result
});
//....
//etc
我是Rx(Js)的新手,并且无法弄清楚如何通过冷却来实现这个热观察。
答案 0 :(得分:5)
您缺少的是安排一项任务,在一段时间后将cachedData
替换为新的AsyncSubject
。以下是如何将其作为新的Rx.Observable
方法:
Rx.Observable.prototype.cacheWithExpiration = function(expirationMs, scheduler) {
var source = this,
cachedData = undefined;
// Use timeout scheduler if scheduler not supplied
scheduler = scheduler || Rx.Scheduler.timeout;
return Rx.Observable.create(function (observer) {
if (!cachedData) {
// The data is not cached.
// create a subject to hold the result
cachedData = new Rx.AsyncSubject();
// subscribe to the query
source.subscribe(cachedData);
// when the query completes, start a timer which will expire the cache
cachedData.subscribe(function () {
scheduler.scheduleWithRelative(expirationMs, function () {
// clear the cache
cachedData = undefined;
});
});
}
// subscribe the observer to the cached data
return cachedData.subscribe(observer);
});
};
用法:
// a *cold* observable the issues a slow query each time it is subscribed
var data = Rx.Observable.return(42).delay(5000);
// the cached query
var cachedData = data.cacheWithExpiration(15000);
// first observer must wait
cachedData.subscribe();
// wait 3 seconds
// second observer gets result instantly
cachedData.subscribe();
// wait 15 seconds
// observer must wait again
cachedData.subscribe();
答案 1 :(得分:0)
一个简单的解决方案是创建一个自定义的管道运算符,以repeatWhen
的持续时间过去。这是我想出的:
export const refreshAfter = (duration: number) => (source: Observable<any>) =>
source.pipe(
repeatWhen(obs => obs.pipe(delay(duration))),
publishReplay(1),
refCount());
然后我像这样使用它:
const serverTime$ = this.environmentClient.getServer().pipe(map(s => s.localTime))
const cachedServerTime$ = serverTime.pipe(refreshAfter(5000)); // 5s cache
重要说明:它使用publishReplay(1)和refCount(),因为shareReplay(1)不会从可观察到的源退订,因此它将永远打在您的服务器上。不幸的是,这样做的结果是,一旦出错,就会从publishReplay(1)
,refCount()
重播错误。即将推出“新的改进” shareReplay。 See notes here on a similar question。一旦有了这个“新”版本,就应该更新此答案-但是自定义运算符的优点是您可以将它们固定在一个位置。