javascript - Hogyan valósítható meg idő alapú lejárattal rendelkező hot observable az RxJS-ben (vagy általánosan a Reaktív Kiterjesztésekben)
Szeretném megvalósítani az Időkorlátos gyorsítótárat RxJs-sel. Íme egy példa a "normál" gyorsítótárra:
//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"*/ });
});
Amikor a subscribe a cachedData-ra először hívódik meg, a "heavy duty job" elindul, és 2 másodperc múlva az eredmény elmentésre kerül a cachedData-ban (AsyncSubject). Bármely további subscribe a cachedData-ra azonnal visszaadja a mentett eredményt (tehát cache implementáció).
Amit el szeretnék érni, az az, hogy egy kis "fűszert" adjak ehhez azzal, hogy a cachedData érvényességi ideje van, és amikor ez az idő lejár, újra szeretném futtatni a "nehéz munkát" az új adatokért, és újra cache-elni ezt az új időszakra, stb...
Kívánt viselkedés:
//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
Új vagyok az Rx(Js)-ben, és nem tudom kitalálni, hogyan valósítsam meg ezt a hot observable-t lehűléssel.
Válasz #1
Csak az hiányzik, hogy ütemezz egy feladatot, amely kicseréli a cachedData-det egy új AsyncSubject-re egy időszak után. Így teheted meg ezt egy új Rx.Observable metódusként:
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);
});
};
Használat :
// 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();
Válasz #2
Egy egyszerű megoldás az lenne, ha létrehoznánk egy egyedi, csőbe fűzhető operátort, hogy a repeatWhen egy adott időtartam elteltével történjen. Íme, amit kitaláltam:
export const refreshAfter = (duration: number) => (source: Observable<any>) =>
source.pipe(
repeatWhen(obs => obs.pipe(delay(duration))),
publishReplay(1),
refCount());
Akkor így használom:
const serverTime$ = this.environmentClient.getServer().pipe(map(s => s.localTime))
const cachedServerTime$ = serverTime.pipe(refreshAfter(5000)); // 5s cache
Fontos megjegyzés: Ez a publishReplay(1), refCount() használatát igényli, mert a shareReplay(1) nem iratkozik le a forrás observable-ről, így örökké fogja terhelni a szerveredet. Sajnos ennek az a következménye, hogy hiba esetén a hiba újra le lesz játszva a publishReplay(1), refCount() által. Hamarosan érkezik egy „új, továbbfejlesztett” shareReplay. Lásd a jegyzeteket itt egy hasonló kérdésnél. Amint ez az „új” verzió elérhetővé válik, ezt a választ frissíteni kell - de a testreszabott operátorok szépsége, hogy egy helyen tudod őket javítani.
