Home > Software engineering >  In angular code, foreach loop skips to the other function before finishing the loop iteration
In angular code, foreach loop skips to the other function before finishing the loop iteration

Time:05-24

private  processArray(evts: Event[]): Promise<void> {
  var auditsvc = this.auditSvc;
  var t = this;

  if(!evts || evts.length ==0 ) {
    return;
  }
  let objArr: any[] = [];

  evts.forEach( function (inpEvt) {   

      auditsvc.getAuditDetails(inpEvt.id).subscribe((data) => {
        
        for(let i=0; i<data.length; i  ){
          let outObj = {};
          
          outObj['Driver'] = inpEvt.driver.name;
          outObj['Executive'] = inpEvt.executive.exec.name;
          outObj['Point of Departure'] = inpEvt.pod;
          outObj['Date of Departure'] = UtilService.dateToString(inpEvt.date);
          outObj['Arrival Time'] = UtilService.timeToString(inpEvt.arrivalTime);
          outObj['Departure Time'] = UtilService.timeToString(inpEvt.departureTime);   
          outObj['Action Timestamp'] = data[i].actionedOnTimestamp;         
          outObj['Modified By'] = data[i].modifiedBy;
          outObj['Comment'] = data[i].comment;
          

          objArr.push(outObj);
          
          
        }    
      }, (err) => {
        console.log(err);
      }, () => {
       
      });
  });

  if(objArr.length == 0) {
    UtilService.openError(t.modalSvc,'No logs found.');
    return;
  }else{
    t.exportAsXLSX(objArr);
  }
}

the for each loop is not executed but the next function is executed first. I want the loop to iterate first and then go to the other function as the loop returns some data required by the other function.

Since the loop is not iterated, the objArr length is 0, it always gives 'No logs found' error.

CodePudding user response:

The problem is that auditsvc.getAuditDetails() is an async operation, so evts.forEach simply iterates over evts, registers the subscriptions—putting them on an async queue—then exits. The subscribe callback functions won't get called until sometime later, after the async operations have completed, and after your current code has already checked objArr.length.

What you need to do is build a pipeline in which objArr.length is checked after all async operations have completed and populated objArr.

One way to do that would be to create an array of Observables by mapping each inpEvt in evts to a corresponding Observable that gets its audit details, like this:

const auditDetails$: Observable<any>[] = evts.map(inpEvt => {
  return auditsvc.getAuditDetails(inpEvt.id).pipe(
    map(data => ({ inpEvt, data })) // <-- need to pass inpEvt also because outObj will need it
  );
});

You can then pass that array of Observables to RxJS forkJoin to trigger them all and return their results in an array:

forkJoin(auditDetails$).pipe( // <-- auditDetails$ is array of Observables
  map((auditDetails: any[]) => { // <-- auditDetails is array of all Observable results, here passed to RxJS map operator
  })
)

You can then use the RxJS map operator to transform the audit details data into the objArr you need.

To do that, inside the RxJS map projection function you can just use native JavaScript Array#map to map the array of audit details to the array of outObjs, like so:

forkJoin(auditDetails$).pipe(
  map((auditDetails: any[]) => { // <-- RxJS map operator
    return auditDetails.map(({ inpEvt, data }) => { // <-- native JS Array#map
      const outObj = {};

      for(let i=0; i<data.length; i  ){
        
        outObj['Driver'] = inpEvt.driver.name;
        // ......
        outObj['Comment'] = data[i].comment;
      }

      return outObj;
    });
  })
)

Finally, subscribe to that Observable pipe and you'll get the objArr in the fully-populated state you need it in:

forkJoin(auditDetails$).pipe(
  map((auditDetails: any[]) => {
    // ...
  })
).subscribe(objArr => { // <-- final result is asynchronously-populated objArr
  if (objArr.length == 0) {
    UtilService.openError(t.modalSvc,'No logs found.');
    return;
  } else {
    t.exportAsXLSX(objArr);
  }
});
  • Related