Каков наилучший способ совершать синхронные вызовы с использованием HttpClient Angular? - PullRequest
1 голос
/ 13 февраля 2020

У меня есть массив объектов следующего типа.

processing-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. Ниже приведен мой сервисный код.

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

Моя цель - вызвать triggerProcessing для каждого элемента processingArray синхронно. Если triggerProcessing вызывается для одного объекта processingArray, мы устанавливаем isProcessingOccured для этого конкретного объекта как true. Если серверная часть возвращает успех для triggerProcessing вызова, который мы сделали для объекта, тогда мы устанавливаем 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 для достижения этой цели. Ниже приведен мой код

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

Это лучший способ для достижения этой цели? Есть ли способ, где я могу полностью избежать использования Promises, async/await и добиться того же синхронного вызова, используя Observables?

Ответы [ 2 ]

0 голосов
/ 13 февраля 2020

Вы можете использовать комбинацию concat, catchError и finalize для обработки ваших запросов по порядку и остановки, как только возникает ошибка:

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);
}
0 голосов
/ 13 февраля 2020

Если вы хотите использовать Observables, как насчет следующего решения. Это не идеально, но я бы сказал, что требование (синхронные вызовы HTTP) также не является.

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

automatic-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);.

...