簡體   English   中英

如何在RxJS(或Reactive Extensions中的常規)中實現可觀察到的時間到期熱

[英]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秒鍾后將結果保存在cachedDataAsyncSubject )中。 任何其他對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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM