循环执行完所有内容后执行代码

时间:2021-05-20 23:31:56

标签: angular typescript async-await

如果我必须提供更多代码或解释,请告诉我。

我正在尝试执行验证,如果验证不成功,请确认(是/否),然后在 for 循环中为所有选定行保存方法。

要求:我在遍历所有选定行后显示成功/失败消息。这意味着:

如果选择了 5 行(2 行有效,3 行无效) - 从 myService.validate 获取

它会询问您是否仍要继续处理 3 个无效的记录 - myConfirmService.confirm

它应该保存那 2 条有效记录

如果用户在确认窗口中选择“是”(假设有 2 条记录),那么它也应该保存这 2 条记录。

最后 - 它将显示成功/失败消息 - alertService.success

我想要的:

为了满足我在需求中描述的内容

并且成功/失败消息应在所有记录保存后仅显示一次

component.ts:

save() {
    this.selectRowData.forEach(async s => {
        this.myService.validate(s.id).subscribe(data => {
            console.log(data);
            if(data == false) {
                this.myConfirmService
                    .confirm('Please confirm..', 'Selected Part do not match expected part data. \n Do you still want to continue? ', 'Yes', 'No')
                    .then((confirmed) => {
                        if(confirmed) {
                            console.log("User selected: Yes");
                            this.myService.savePart();
                        } else {
                            console.log("User selected: No");
                        }
                    })
                    .catch(() => {
                        console.log("Error...");
                    });
            } else {
                console.log("Selected Rule matches expected rules for: " + s.designChangeClass);
                this.saveModelPart(s);
            }
        }, (error: any) => {
            alert("Error Linking Lot");
        });
    });
    console.log('1st After for loop');
    this.alertService.success('Part saved successfully.')
}

service.ts

validate(id: string): Observable < boolean > {
    return this.httpClient.get < boolean > ('/RestWeb/validate?id=' + id);
}
      
validate1(id: string) {
    return this.httpClient.get < boolean > ('/RestWeb/validate?id=' + id).toPromise();
}
  
savePart(item: Part): Observable<any> {
    return this.httpClient.post('/RestService/savePart', item);
}

4 个答案:

答案 0 :(得分:3)

给你。

//Change method to async
async save() {
  //Create custom index variable, since for of loop doesn't have one
  let ind = 0;
  //Use for of instead of forEach
  for (const eachRow of this.selectRowData) {
    //await until API validated
    const isValid = await this.myService.validate(eachRow.id).toPromise();
    if (!isValid) {
      //await again if it is not valid and until user confirmed
      const isConfirmed = await this.myConfirmService.confirm('Please confirm..', 'Selected Part do not match expected part data. \n Do you still want to continue? ', 'Yes', 'No').toPromise();
      if (isConfirmed) {
        //await again until record saved as user selected yes in confirm
        console.log('User selected: Yes');
        await this.myService.savePart().toPromise();
      } else {
        console.log('User selected: No');
      }
    } else {
      console.log(
        'Selected Rule matches expected rules for: ' +
          eachRow.designChangeClass
      );
      this.saveModelPart(eachRow);
    }
    //Finally display message after loop is finished
    if (ind === this.selectRowData.length - 1) {
      this.alertService.success('Part saved successfully.');
    }
    ind++;
  }
}

尽管此解决方案有效,但以这种方式进行操作并不是一个好习惯,
相反,修改 API 以接受多个 ids 进行验证,并显示一个记录表(成功和失败)以及用于确认继续的按钮。

答案 1 :(得分:1)

在这种情况下,您可以使用 fork join,因此它会进行所有调用并获得对布尔数组的响应

save() {
  let obsArray = this.selectRowData.map(s=>this.myService.validate(s.id));
  forkJoin(obsArray).subscribe(
    (arrayBoolean)=>{
       // do your logic here
    }
  )
 
}

查看https://www.learnrxjs.io/learn-rxjs/operators/combination/forkjoin了解更多信息

//编辑:拼写错误

答案 2 :(得分:0)

据我所知,您试图仅保存有效数据并额外进行用户确认。 One Part 有点令人困惑,因为您正在尝试确认与预期规则不匹配的有效数据,但除此之外,我会做这样的事情。

组件方法:

IAmazonSecurityTokenService stsClient = new AmazonSecurityTokenServiceClient();
string accountId=stsClient.GetCallerIdentityAsync(new GetCallerIdentityRequest()).Result.Account;

确认服务方式:

  rowValidation() {
    this.selectRowData.forEach( rowElement => {
      this.myService.validate(rowElement.id).subscribe( data => {
        //call comfirm service method.
      })
    })
  }

答案 3 :(得分:0)

好吧朋友……你的逻辑实际上有点复杂,所以如果我们把它分解成更小的部分会更容易理解。

如果我正确理解您的要求,您需要:

  1. 验证每一行
  2. 如果验证失败,请询问用户是否仍要保存
  3. 保存数据(有效行,可能还有无效行

从终点开始,我们可以这样做:

  save() {
    of(this.selectRowData).pipe(
      switchMap(this.validateRows),
      switchMap(rows => this.promptUserIfSomeInvalid(rows).pipe(
        switchMap(saveInvalidRows => {
          const rowsToSave = saveInvalidRows ? rows : rows.filter(r => r.isValid);
          
          return forkJoin(rowsToSave.map(this.myService.savePart));
        })
      ))
    ).subscribe();
  }

这里我们以 of 开始 observable 链。

您可能已经注意到所有的switchMap!!!如果您不熟悉,switchMap 是一个漂亮的操作符,它会为您订阅一个 observable,并发出它的排放。它还负责自动取消订阅这个“内部可观察对象”。

您需要了解的有关 switchMap 的信息:

<块引用>

switchMap 中,您传递一个返回 observable 的函数。

第一个 switchMap 接收行数据并订阅一个调用验证 observable 的 observable。 this.validateRows 返回一个发出行数组的 observable,每个行都有一个额外的布尔属性,指示该行的验证是否通过。

switchMap #2 订阅一个 observable,它发出一个布尔值,指示是否应该保存无效行。

switchMap #3 订阅一个 observable,它使您的所有 api 调用都保存适当的行。

forkJoin 创建一个 observable,当它的源 observable 数组全部完成时发出。因此,我们只是将行mapping 到可观察对象 (this.myService.savePart())。

以下是上述方法的实现:

  private validateRows(rows: Row[]) {
    return forkJoin(rows.map(row => this.myService.validate(row.id))).pipe(
      map(this.appendIsValidFieldToRows(rows)),
    );
  }

  private promptUserIfSomeInvalid(rows: ValidatedRow[]) {
    const invalidCount = rows.filter(r => !r.isValid).length;
    const message = `Would you like to save ${invalidCount} rows?`;

    return rows.some(r => !r.isValid)
      ? from(this.myConfirmService.confirm(message))
      : of(true);
  }

  private appendIsValidFieldToRows(rows: Row[]) {
    return (isValidArray: boolean[]) => rows.map((r, i) => ({...r, isValid: isValidArray[i]})) as ValidatedRow[];
  }

这是一个 Non-Functional StackBlitz,我用它作为便笺簿来避免打字错误。它可能有助于查看类型甚至将一些虚假数据放入测试中。

我不能 100% 确定我理解您的确切意图,但我希望这能让您朝着正确的方向前进。

主要内容是:

  • 将代码分解成更小的函数
  • 利用 switchMap 避免嵌套订阅。
  • 不使用 forEach 循环,而是使用 Array.prototype.map 将行数组转换为 Observable 数组,然后您可以将其传递给 forkJoin

注意:我忽略了您的错误捕获,因为这个答案很长而且问题并没有真正关注这一方面。但是,由于逻辑分解为单独的函数,因此可以轻松地在必要的位置添加 catchError