rxjs repeat api call based on duration specified in response

…衆ロ難τιáo~ 提交于 2019-12-24 17:01:53

问题


Context

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.

What I tried

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>

Question

Using rxjs, how can I make an api call and repeat it on a delay based on its last response?

UPDATE

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>

回答1:


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.




回答2:


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));


来源:https://stackoverflow.com/questions/49886776/rxjs-repeat-api-call-based-on-duration-specified-in-response

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!