按顺序处理请求

Process request sequentially

主要目标是按顺序处理我的请求。我输入了扫描仪快速扫描值的输入。但有时服务器 returns 数据很慢,如果我扫描速度非常快,服务器就会崩溃 returns 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顺序处理请求,但我不知道我应该如何处理请求以及我应该如何选择方向。

我不是 Angular 人,但会帮助您 Javascript 解决您的问题。您的问题是 Javascript promises 的教科书示例。在Javascript中,一个Promise是一个对象,它可能在将来的某个时候产生一个单一的值。如果您从未听说过这个概念,我敢肯定上面的陈述很难理解,但让我们用常识来思考一下。 Javascript 中的 Promise 与 real-life 中的承诺并无不同。如果我向你保证,可能会发生以下情况:

  • 我答应你的时候已经兑现了,这样你就可以处理了
  • 我的承诺会在未来的某个时候实现
  • 以后我的承诺没有兑现就很明显了
  • 我的承诺永不评价

当您使用 promises 时,这些直观的案例在编程中有类似的案例。

承诺示例:

new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000); // (*)

});

示例取自 https://javascript.info/promise-chaining

如您所见,Promise 构造函数获取一个 function 参数,而该参数又具有一个 resolve 和一个 reject 参数。 resolvereject都是functionsresolve 在满足 Promise 时调用,而 rejectPromise 被拒绝时调用。在我们的例子中,Promise 的主体包含对 setTimeout 的调用,它确保 Promise 的解析比 Promise 的创建晚一秒。

那么,如果一个请求是 Promise(直觉上这是一个承诺,无论它是什么,你都会得到响应)并且下一个请求是下一个 Promise 等等?有没有办法链接承诺?当然,让我们看看完整的例子:

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;

});

正如您可能已经猜到的那样,您的请求应该在承诺的主体中,通过调用 then() 链接起来。例如:

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

像这样链接您的 promise 非常简洁并且易于理解和维护。当然,这更像是一个理论上的答案,因为您需要定义 resolverejectpromiseBodyrequests 在您的特定情况下看起来相似,并且您将需要应对其中一个请求由于某种原因永远不会得到答复的可能情况,但是一旦您了解您有一种非常优雅的方式继续进行并且只需要弄清楚细节,您就会正确的轨道。

当然,您可以通过链接回调以传统方式解决此问题,但这不是很优雅。您也可以使用 Javascript 生成器来解决这个问题,您最终会得到比旧式回调 (hell) 解决方案更优雅的东西,例如:

function *myRequestHandler(request) {
    while (request) {
        send(request);
        request = yield;
    };
}

并确保在调用时传递正确的 request,并且每当执行回调时,都会调用 next(),传递 next request

据我了解您的问题,您需要按顺序执行 api 请求 onSubmit。 在你的情况下,你需要 rxjs Subject 来实现预期的行为。我修改了您的组件,因此您可以查看 ngOnInit 和 onSubmit 函数。

makeRequest$ is now Observable, where new events arrive on every onSubmit method call. You can use concatMap to sequentially send api requests and also you can use delay operator to pause for some time after request has finished so that server is not getting hit too much.

另请查看 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();
  }
}