[英]How to implement time expiry hot observable in RxJS (or general in Reactive Extensions)
我想用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"*/ });
});
首次調用cachedData
上的subscribe
,將調用“繁重的工作”,並在2秒鍾后將結果保存在cachedData
( AsyncSubject
)中。 任何其他對cachedData
后續subscribe
cachedData
立即返回保存的結果(因此實現了緩存)。
我想要實現的是在cachedData
內的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)的新手,無法解決如何通過冷卻實現此可觀察的熱點。
您所缺少的只是安排一個任務,在一段時間后用新的AsyncSubject
替換您的cachedData
。 這是作為新的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();
一個簡單的解決方案是創建一個自定義的管道運算符,以在持續時間過去后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。 有關類似問題,請參見此處的注釋 。 一旦有了這個“新”版本,就應該更新此答案-但是自定義運算符的優點是您可以將它們固定在一個位置。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.