在 angular 代码中,foreach 循环在完成循环迭代之前跳到另一个函数

In angular code, foreach loop skips to the other function before finishing the loop iteration

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

没有执行for each循环,而是先执行下一个函数。我希望循环先迭代然后转到另一个函数作为循环 returns 其他函数所需的一些数据。

由于循环没有迭代,objArr长度为0,总是报'No logs found'错误

问题在于 auditsvc.getAuditDetails() 是一个异步操作,因此 evts.forEach 只是迭代 evts,注册订阅——将它们放入异步队列——然后退出。 subscribe 回调函数直到稍后某个时间才会被调用,在异步操作完成之后,并且在您当前的代码已经检查了 objArr.length.

之后

您需要做的是构建一个管道,其中检查 objArr.length 所有异步操作完成并填充后 objArr.

一种方法是通过将 evts 中的每个 inpEvt 映射到获取其审计详细信息的相应 Observable 来创建一个 Observable 数组,如下所示:

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

然后您可以将该 Observable 数组传递给 RxJS forkJoin 以触发它们并将 return 它们的结果放在一个数组中:

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

然后您可以使用 RxJS map 运算符将审计详细信息数据转换为您需要的 objArr

为此,在 RxJS map 投影函数中,您可以使用原生 JavaScript Array#map 将审计详细信息数组映射到 outObj 数组s,像这样:

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

最后,订阅 Observable 管道,您将在 fully-populated 状态下获得 objArr,您需要它:

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