主要目标是按顺序处理我的请求。我已经输入了快速扫描仪扫描值的输入。但是有时服务器返回数据的速度很慢,如果我扫描得很快,服务器就会掉下来并返回500。我的想法是按顺序处理此请求。我试图找到一些解决方案,但没有找到我想要的。我不知道该使用拦截器,还是将其保存在服务或组件中。
我试图在拦截器中找到解决方案,但是在请求完成后,拦截器可以处理下一个请求时,我没有发现。我尝试使用rxjs运算符,但是我不知道如何将其合并到我的解决方案中。这是我要在解决方案中使用的示例
from([1, 2, 3, 4])
.pipe(
concatMap(param =>
this.http.get(
`http://localhost:6013/api/v1/test/buffer?data=${param}`
)
)
)
.subscribe(res => console.log(res));
从操作员处观察到我的请求,然后ConcatMap对其进行顺序处理
有我的骨架应用
app.component.ts
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormBuilder, Validators } from '@angular/forms';
import { Subject } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
form = this.fb.group({
input: ['', Validators.required]
});
makeRequest$ = new Subject();
constructor(private http: HttpClient, private fb: FormBuilder) {}
onSubmit() {
this.http
.get(
`http://localhost:6013/api/v1/test/buffer?data=${
this.form.controls.input.value
}`
)
.subscribe(res => {
console.log(res);
});
this.form.reset();
}
ngOnInit() {}
}
app.interceptor.ts
import { Injectable } from '@angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpEvent
} from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class AppInterceptor implements HttpInterceptor {
arrayRequest: Array<HttpRequest<any>> = [];
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(req);
}
}
app.components.html
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<label>
Data:
<input type="text" formControlName="input" />
</label>
<button type="submit" [disabled]="!form.valid">Submit</button>
</form>
我除外上面的代码。 ConcatMap按顺序处理请求,但我不知道如何处理请求以及应该如何选择方向。
答案 0 :(得分:2)
据我所知,您需要按顺序执行api请求onSubmit。 您需要使用rxjs Subject来实现预期的行为。我修改了您的组件,以便您可以查看ngOnInit和onSubmit函数。
makeRequest $现在是可观察的,每个新事件都到达 onSubmit方法调用。您可以使用concatMap顺序发送api 请求,您也可以使用延迟运算符暂停一段时间 请求完成后,服务器不会受到太大影响。
还可以查看 unsubscribe $和takeUntil(this.unsubscribe $) <-这些用于在组件销毁后释放内存,用于避免内存泄漏。这是最佳做法之一。
import { Component, OnInit } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { FormBuilder, Validators } from "@angular/forms";
import { Subject } from "rxjs";
import { concatMap, takeUntil, delay } from "rxjs/operators";
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.scss"]
})
export class AppComponent implements OnInit {
public form = this.fb.group({
input: ["", Validators.required]
});
private makeRequest$ = new Subject();
private unsubscribe$ = new Subject();
constructor(private http: HttpClient, private fb: FormBuilder) {}
ngOnInit() {
this.makeRequest$
.pipe(
delay(500),
concatMap(item =>
this.http.get(`http://localhost:6013/api/v1/test/buffer?data=${item}`)
),
takeUntil(this.unsubscribe$)
)
.subscribe();
}
ngOnDestroy(): void {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
public onSubmit() {
this.makeRequest$.next(this.form.controls.input.value);
this.form.reset();
}
}
答案 1 :(得分:0)
我不是Angular的人,但是可以用一个整洁的Javascript解决方案来帮助您。您的问题是Javascript promises的教科书示例。在Javascript中, Promise
是一个对象,将来可能会产生单个值。如果您从未听说过这个概念,我敢肯定,上面的陈述很难理解,但让我们以常识来思考。 Javascript中的Promise
与现实生活中的承诺没有什么不同。如果我向您保证,会发生以下情况:
当您使用诺言时,这些直观的案例在编程中具有类似的案例。
承诺示例:
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000); // (*)
});
示例取自https://javascript.info/promise-chaining
如您所见,Promise构造函数获得一个function
参数,该参数又具有一个resolve
和一个reject
参数。 resolve
和reject
均为functions
。满足{{1}时调用resolve
,而拒绝Promise
时调用reject
。在我们的案例中,Promise
的主体包含对Promise
的调用,该调用确保setTimeout
的解析比创建Promise
的迟一秒钟。
那么,如果一个请求是一个Promise
(直觉上是一个承诺,无论它是什么,您都会得到一个响应),下一个请求是下一个Promise
,以此类推呢?有没有办法兑现承诺?当然,让我们看完整的示例:
Promise
您可能已经猜到了,您的请求应该在应许的主体中,并通过调用then()进行链接。例如:
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000); // (*)
}).then(function(result) { // (**)
alert(result); // 1
return result * 2;
}).then(function(result) { // (***)
alert(result); // 2
return result * 2;
}).then(function(result) {
alert(result); // 4
return result * 2;
});
兑现这样的诺言非常简洁,易于理解和维护。当然,这更多是理论上的答案,因为您将需要定义function promiseBody(resolve, reject) {
// send request
// if successful, call resolve
// if failed, call reject
}
var prom = new Promise(promiseBody);
//this array contains requests to be called after the first
for (var req of requests) {
prom = prom.then(promiseBody);
}
,resolve
,reject
和promiseBody
在您的特定情况下的外观,以及您将需要应对可能由于某种原因而永远无法回答其中一个请求的可能情况,但是一旦您了解自己有一个非常优雅的方式来进行并且只需要弄清楚细节,您就可以正确的轨道。
当然,您可以通过链接回调以传统方式解决此问题,但这并不是很优雅。您还可以使用Javascript生成器解决此问题,最终会得到比老式回调(hell)解决方案更优雅的东西,例如:
requests
,并确保在调用适当的function *myRequestHandler(request) {
while (request) {
send(request);
request = yield;
};
}
时以及每次执行回调时都传递request
,并传递next()
next
。