简体   繁体   中英

Synchronously process Observable to prevent too many HTTP requests from timing out using rxjs Angular and ngrx

I have the following code. It gets an array of Patients and builds a rows object this.rows that I show in a table on the frontEnd of this angular 4 component (I am also using rxjs 5.5).

My issue is the hasAlert property of each row is assigned by a call to hasAlerts(). In the HasAlerts method, I make an http request, for each patient via this.dataService.fetchItems<Observation>( .

When there are many patients, too many HTTP requests will occur asynchronously and they will start to fail (timeout) from HasAlerts(). Is there a way to throttle this,or process one Observable at a time from hasAlerts()?

Below are possible ways to solve this

  1. I think concatMap may be helpful but I am not sure how to use it. Also should use it to process each patient/row or should try and aggregate each hasAlert() observable into a list and then try and use concatMap to process them one at a time?
  2. A workaround is the patientsAlertsProcessed variable wrapper that I added with the comment // is done to throttle the hasAlertsMethod. I limit it to only hit that method for 15 times to avoid timeout issues. This is not ideal because I am not able to start it up again once those 15 async requests complete.

Code is below

ngOnInit(): void {
    this.store.dispatch(new patients.Load([]));

    this.patients$ = this.store.select(fromPatients.getAll); 
    var patientsAlertsProcessed =0;
    this.patients$.debounceTime(2000).map(p =>{//Emits Observable<Patient[]>s, the debounceTime is to take the last one as it builds up
      this.rows = p.map(pat => {// p = Patient[], iterate through the array of patients
        var observations=0;
        var rowX= {
          username: pat.username,
          id: pat.id,
          hasAlert:false, 
        };

        if (patientsAlertsProcessed<15){// this is done to throttle the HasAlerts Method
          this.hasAlerts(pat).do(x => {
            observations++;
            if (observations>0)
            {
              rowX.hasAlert=true;
            }
          }).subscribe();
          patientsAlertsProcessed++;
        }
        return rowX;
      });
    }).subscribe(
       ()=> { },
       ()=> {
        this.table.recalculatePages();
      }
    );

  }
  hasAlerts(pat: Patient): Observable<Observation> {
    var obs$= this.dataService.fetchItems<Observation>(// this is making an HTTP get request 
        "Observation",
        null,
        pat.id
      ).filter(function (x){
        if (x.category.coding[0].code == "GlucoseEvent"){ 
            return true;
        }
        else{
          return false; 
        }
      }
    );
    return obs$;
  }

You may want to try something along these lines.

const concurrency = 10; 
const rowsWithAlerts = [];

this.patients$.debounceTime(2000)
.switchMap(patients => Observable.from(patients)) // patients is of type Patient[]
.mergeMap(patient => {
   rowsWithAlerts.push(patient);
   this.hasAlerts(patient).do(
   hasAlertsResult => {
     // here you have hold to both the patient and the result of
     // hasAlerts call
     patient.hasAlerts = hasAlertsResult;
   }}), concurrency)
.subscribe(
   () => this.rows = rowsWithAlerts;
)

The key here is to use the mergeMap operator with the concurrency level set to a value, in this example 10 but clearly can be anything.

This allows to limit the number of observables to which you subscribe at the same time, which in your case means to limit the number of http calls you make.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM