Hit URL for Multiple times for maximum one Minute in angular

Viewed 318

I have a function in angular which return data set with status "Continue" or "finished". I want to keep hitting the call until status returns "finished" but for maximum one minute only. How can I achieve this in angular

public getLeadsResponse(key: any) {
    this._service.getLeadsData(key).subscribe((response: any) => {
        if (response) {
            if (response.payload.status == "running") {
    // here if i repeate this function it will start an infinite loop if status is always "running"
                this.getLeadsResponse(key);
            }
            else if (response.payload.status == "finished") {
                this.items = res.payload.result;
            }
        }
    })
}
3 Answers

You can do that using expand():

import { of, EMPTY, timer } from 'rxjs'; 
import { expand, map, filter, takeUntil } from 'rxjs/operators';

const makeHttpRequest$ = timer(1000).pipe(map(() => Math.random() > 0.9 ? 'finished' : 'continue'));

const source = of(null).pipe(
  expand(value => value === 'finished' ? EMPTY : makeHttpRequest$),
  filter(value => value !== null), // Ignore the first `of(null)` that triggered the chain.
  takeUntil(timer(60 * 1000)),
);

source.subscribe(console.log);

Live demo: https://stackblitz.com/edit/rxjs-8n11jj?devtoolsheight=60

In addition to @martin's answer, you could also do a explicit timestamp check for 1 minute using RxJS timestamp operator.

I've used RxJS timer to emit every 'n' timeunits and switch to the HTTP request for it's each emit using switchMap operator.

Try the following

import { timer } from "rxjs";
import { filter, map, timestamp, switchMap } from "rxjs/operators";

public getLeadsResponse(key: any) {
  const now = Date.now();

  const subscription = timer(0, 6000).pipe(  // <-- adjust polling frequency
    switchMap(_ => this._service.getLeadsData(key)),
    timestamp(),
    map((data: { timestamp: number; value: any }) => {
      if (data.timestamp - now < 60000) {               
        return data.value;
      } else {
        subscription.unsubscribe();  // <-- close subscription if 1 min has elapsed
      }
    }),
    filter(response => response.payload.status == "finished")
  ).subscribe({
    next: response => {
      this.items = res.payload.result;
      subscription.unsubscribe();  // <-- close subscription if status is "finished"
    },
    error: error => {
      // handle error
    }
  });
}

I think you only can add a timeout to your ajax query which will "break" the connection to the server if it happens. So you won't be able to test payload result value.

You may implement the desired behavior in other ways :

  • make your backend process asynchonous, sou you can perform ajax query let say every 5 seconds to check current status and the route you reach have to respond quickly and just "check operation status". then the 1 minute max check logic will be in your frontend app

  • open a long polling connection that is able to send multiple message using for exemple SSE

  • Do the same thing above using websocket.

In all cases the real design in this is that your backend server operation have to be asynchronous and it can communicate it's current status to another process / thread.

Note that at some point when designing an api you ends up doing this kind of stuff. Adding some asynchronous processing in a project is not as easy as doing simple long queries and add complexity to a project, but it has some benefits such as not overloading servers and let user interfaces be more reactive.

Related