使用 Angular 的 HttpClient 进行同步调用的最佳方法是什么?

What is the best way to make synchronous calls using Angular's HttpClient?

我有一组以下类型的对象。

处理-details.model.ts

export class ProcessingDetails{
     id: string;
     isProcessingOccured: boolean;
     isProcessingSuccessful:boolean;
     constructor(id,isProcessingOccured,isProcessingSuccessful) {
          this.id = id;
          this.isProcessingOccured = isProcessingOccured;
          this.isProcessingSuccessful = isProcessingSuccessful;
     }
}

数组是根据某些输入动态生成的。数组生成后如下所示。

processingArray = [{id:1, isProcessingOccured: false, isProcessingSuccessful: false},
                   {id:2, isProcessingOccured: false, isProcessingSuccessful: false},
                   .....
                   {id:k, isProcessingOccured: false, isProcessingSuccessful: false}]

我有一个 REST 端点,它采用 ProcessingDetails 类型的对象。以下是我的服务代码。

自动化-batch.service.ts

@Injectable()
export class AutomatedBatchService {
     constructor(@Inject('baseUrl') private baseUrl: string, private httpClient: HttpClient) {}

     public triggerProcessing(batchDetails:ProcessingDetails) {
         return this.httpClient.post(`${this.baseUrl}`,batchDetails);
     }
}

我的目标是以同步方式为 processingArray 的每个元素调用 triggerProcessing。如果在 processingArray 的一个对象上调用 triggerProcessing,我们将在该特定对象上设置 isProcessingOccured 作为 true。如果我们在对象上进行的 triggerProcessing 调用的后端 returns 成功,那么我们将 isProcessingSuccessful 设置为 true。例如,假设 id = 1 和 id = 2 的处理成功。数组必须如下所示:

    processingArray = [{id:1, isProcessingOccured: true, isProcessingSuccessful: true},
                       {id:2, isProcessingOccured: true, isProcessingSuccessful: true},
                       {id: 3, isProcessingOccured: false, isProcessingSuccessful:false }
                       .....
                       {id:k, isProcessingOccured: false, isProcessingSuccessful: false}]

如果一个对象的处理失败,我们不能处理数组的剩余部分。例如,如果对象 {id: 3, isProcessingOccured: false, isProcessingSuccessful:false } 处理失败,我们不能触发从 {id: 4, isProcessingOccured: false, isProcessingSuccessful:false } 开始的服务调用。

我目前正在使用 async/await 来实现这一点。以下是我的代码

处理-test.component.ts

import { Component } from "@angular/core";
@Component({
     selector: 'processing-test',
     templateUrl: './processing-test.component.html',
     styleUrls:['./processing-test.component.css'],
     providers: [AutomatedBatchService]
})
export class ProcessingTestComponent {
     constructor(private automatedBatchService: AutomatedBatchService) { }
     public isSuccess:boolean = true;
     public processingArray: Array<ProcessingDetails>= [];
     async startBatchRun() {
         for( var i = 0; i < this.processingArray.length; i++ ) {
              if(this.isSuccess){
                 await this.automatedBatchService.triggerProcessing(this.processingArray[i])
                 .toPromise()
                 .then(res => {
                     this.processingArray[i].isProcessingOccured = true
                     this.processingArray[i].isProcessingSuccessful = true
                 })
                 .catch(rej => {
                     this.isSuccess = false
                     this.processingArray[i].isProcessingOccured = true
                     this.processingArray[i].isProcessingSuccessful = false
                 });

             }else {
                 break;
             }            
         }
     }
}

这是实现此目标的最佳方法吗?有什么方法可以完全避免使用 Promisesasync/await 并使用 Observables 实现相同的同步调用?

如果你想使用Observables,下面的解决方案怎么样。它并不完美,但我想说要求(同步 HTTP 调用)也不完美。

处理-test.component.ts

import { Component, OnInit, OnDestroy } from "@angular/core";
import { Subscription } from 'rxjs';

@Component({
     selector: 'processing-test',
     templateUrl: './processing-test.component.html',
     styleUrls:['./processing-test.component.css'],
     providers: [AutomatedBatchService]
})
export class ProcessingTestComponent {
  public isSuccess = true;
  public processingArray: Array<ProcessingDetails>= [];
  private processSubscription: Subscription;

  constructor(private automatedBatchService: AutomatedBatchService) { }

  ngOnInit() {
    this.batchRun();
  }

  private batchRun() {
    let i = 0;
    if (i < this.processingArray.length) {
      this.processSubscription = this.automatedBatchService.triggerProcessing(this.processingArray[i]).subscribe(
        response => {
          this.processingArray[i].isProcessingOccured = true;
          this.processingArray[i].isProcessingSuccessful = true;
          i++;
          this.batchRun();
        },
        error => {
          this.isSuccess = false;
          this.processingArray[i].isProcessingOccured = true;
          this.processingArray[i].isProcessingSuccessful = false;
        }
      );
    }
  }

  ngOnDestroy() {
    if (this.processSubscription) {
      this.processSubscription.unsubscribe();
    }
  }
}

自动化-batch.service.ts

import { Subject, Subscription } from "rxjs";

@Injectable()
export class AutomatedBatchService {
  private httpSubscription: Subscription;

  constructor(@Inject('baseUrl') private baseUrl: string, private httpClient: HttpClient) {}

  public triggerProcessing(batchDetails:ProcessingDetails) {
    const result = new Subject<number>();

    if (this.httpSubscription) {
      this.httpSubscription.unsubscribe();
    }
    this.httpClient.post(`${this.baseUrl}`, batchDetails).subscribe(
      response => { result.next(response.status); },
      error => { result.error(error.status); }
    );

    return result.asObservable();
  }

}

这种方法的一个优点是,由于我们为连续的元素重复调用该函数,因此如果需要,我们可以使用 setTimeout() 引入显式延迟。例如。在 batchRun() 函数中,调用将是 setTimeout(() => { this.batchRun(); }, 2000);.

您可以使用 concatcatchErrorfinalize 的组合来按顺序处理您的请求,并在发生错误时立即停止:

public process(batch: ProcessingDetails[]) {
  const processes = batch.map(details => this.httpClient.post(`${this.baseUrl}`, batchDetails).pipe(
    map(() => {
      details.isProcessingSuccessful = true;
      return batch;
    }),
    catchError(() => {
      details.isProcessingSuccessful = false;
      return throwError(false);
    }),
    finalize(() => {
      details.isProcessingOccured = true;
    }),
  ));
  return concat(...processes);
}