rxjs 7: Redo requests after a certain time / cache expiration #6264
-
Initially I reported this as a bug, as it turned out, it previous was a bug and is now fixed in rxjs 7. So I'm just reiterating my question here, hoping that someone has a good solution :) In rxjs v7 This was usually done with With You can pass a windowTime to Example how it was working before:
rxjs: 6.6.3 Example with shareReplay
rxjs": "^7.0.0-rc.2 Note how the Observable doesn't emit anything anymore, after 5sec. Expected behaviour:Requests are repeated once the windowTime is exceeded. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 7 replies
-
Hi @StefanKern One thing that can be a bit surprising about So, since your source is an Observable that emits once and then it completes, then I would suggest enhancing it so that it doesn't complete, i.e: Ok, sure, but then how do we make sure that the cached value stays alive for a certain amount of time 🤔? I suggest using a pipeable operator like this: import { Observable, noop } from 'rxjs'
const delayLatestUnsubscription = (ms: number) => <T>(source$: Observable<T>) => {
let token = 0
let cleanupFn: (() => void) = noop
return new Observable<T>(observer => {
// Notice that it's necessary to create a new Observer, otherwise `take` would
// close the original one and immediately kill the subscription
const subscription = source$.subscribe(
(v) => {
observer.next(v)
},
(e) => {
observer.error(e)
},
() => {
observer.complete()
}
)
cleanupFn()
return () => {
cleanupFn = () => {
clearTimeout(token)
cleanupFn = noop
subscription.unsubscribe()
}
token = setTimeout(cleanupFn, ms)
}
})
} @Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
name = "Angular " + VERSION.major;
urlofApi = "https://api.github.com/search/repositories?q=helloWorld";
testX = concat(
this.http.get(this.urlofApi),
NEVER
).pipe(
tap(() =>
console.log(
`++ api was newly called at ${new Date().toLocaleTimeString()} ++`
)
),
shareReplay({
bufferSize: 1,
refCount: true
}),
delayLatestUnsubscription(5000),
);
constructor(private http: HttpClient) {}
test() {
console.log("Requesting data from API");
this.testX.pipe(take(1)).subscribe(
(value) => {
console.log("value received", value)
}
);
}
} I think that this solution has the behavior that you were expecting and it works on both RxJS 6 and 7. I'm sure that there are other ways to get the same behavior, but this is the first one that came to mind. I hope this helps. |
Beta Was this translation helpful? Give feedback.
-
FWIW - as I mentioned in the issue you opened, I don't really have the time to give you a proper answer, ATM - this |
Beta Was this translation helpful? Give feedback.
Hi @StefanKern
One thing that can be a bit surprising about
shareReplay
withrefCount
is that if the the source completes, then that's it: the resulting observable will never re-subscribe to the source, and every time that a new subscriber comes: it will replay the latest value(s) and then complete. I once opened an issue because I thought that was a bug 😅.So, since your source is an Observable that emits once and then it completes, then I would suggest enhancing it so that it doesn't complete, i.e:
concat(source, NEVER)
Ok, sure, but then how do we make sure that the cached value stays alive for a certain amount of time 🤔?
I suggest using a pipeable operator like this: