使用 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;
}
}
}
}
这是实现此目标的最佳方法吗?有什么方法可以完全避免使用 Promises
、async/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);
.
您可以使用 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);
}
我有一组以下类型的对象。
处理-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;
}
}
}
}
这是实现此目标的最佳方法吗?有什么方法可以完全避免使用 Promises
、async/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);
.
您可以使用 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);
}