Reputation: 5988
I need to get data from the server that has a variable expiration (specified in the response). Once it expires I need to get it again. So I would like to create a stream that makes an asynchronous request and repeats that request after a time that is specified in the response of that request.
Here is my first attempt but the repeatWhen
doesn't have access to the last response. Instead of doing the repeat every 1000ms I would like to do it based on the expiration property on the response.
const { Observable, defer, of } = rxjs;
const { repeatWhen, delay, map, take } = rxjs.operators;
let count = 0;
function api() {
return of({ data: count++, expiration: Math.random() * 1000 });
}
defer(() => api()).pipe(
repeatWhen((notification) => notification.pipe(delay(1000))),
map((response) => response.data),
take(5)
).subscribe((x) => { console.log(x); });
<script src="https://unpkg.com/rxjs@rc/bundles/rxjs.umd.min.js"></script>
Using rxjs, how can I make an api call and repeat it on a delay based on its last response?
This technically does what I want but it is a bit hacky... so I would like a better solution.
const { Observable, defer, of, BehaviorSubject, timer } = rxjs;
const { repeatWhen, delay, map, take, tap, switchMap } = rxjs.operators;
let count = 0;
function api() {
return of({ data: count++, expiration: Math.random() * 1000 });
}
const trigger = new BehaviorSubject(0);
trigger.pipe(
switchMap((expiration) => timer(expiration)),
switchMap(() => api().pipe(
tap((response) => { trigger.next(response.expiration); })
)),
take(5)
).subscribe((x) => { console.log(x); });
<script src="https://unpkg.com/rxjs@rc/bundles/rxjs.umd.min.js"></script>
Upvotes: 5
Views: 2163
Reputation: 8478
This can be easily achieved using the .expand()
operator, which is meant for recursive purposes. The exact solution is only a few lines:
api()
.expand(({expiration}) => api().delay(expiration))
.take(5)
.subscribe(x=>console.log(x));
Here is the JSBin.
Upvotes: 4
Reputation: 17762
I am not sure I understood completely your question, but what about something like this
function api() {
return of(Math.random() * 10000);
}
defer(() => api()).pipe(
tap(delay => console.log('delay', delay)),
switchMap(data => interval(data)),
take(5)
).subscribe(console.log);
UPDATED ANSWER AFTER COMMENT
You are already doing the repetition based on what the api is returning to you, and not every 1000 ms. If you run this code it should be clear
let count = 0;
const expiration = Math.random() * 1000;
function api() {
return of({ count, expiration});
}
defer(() => api()).pipe(
tap(delay => console.log('delay', delay.expiration)),
switchMap(data => interval(data.expiration).pipe(map(() => data))),
map(data => ({expiration: data.expiration, count: count++})),
take(5)
).subscribe(console.log);
SECOND UPDATE AFTER SECOND COMMENT
If I understand now what you want to achieve, this should help you
defer(() => api()).pipe(
tap(data => console.log('I do something with this data', data)),
switchMap(data => interval(data.expiration)),
switchMap(() => api()),
take(5)
).subscribe(data => console.log('I do something with this data', data));
Upvotes: 0