Обрабатывать запрос последовательно - PullRequest
4 голосов
/ 14 мая 2019

Основной целью является последовательная обработка моих запросов. У меня есть вход для сканирования значений сканером в быстром порядке. Но иногда сервер возвращает данные медленно, и если я сканирую очень быстро, сервер падает и возвращает 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 обрабатывает запрос последовательно, но я не знаю, как мне следует работать с запросами и как мне выбирать.

Ответы [ 2 ]

2 голосов
/ 15 мая 2019

Насколько я понимаю, ваша проблема заключается в том, что вам необходимо последовательно выполнять запросы API на Submmit. В вашем случае вам нужен rxjs Subject для достижения ожидаемого поведения .Я изменил ваш компонент, чтобы вы могли взглянуть на функции ngOnInit и onSubmit.

makeRequest $ теперь можно наблюдать, где новые события поступают при каждом вызове метода onSubmit.Вы можете использовать concatMap для последовательной отправки запросов API, а также вы можете использовать оператор задержки, чтобы сделать паузу на некоторое время после завершения запроса, чтобы сервер не получил слишком много ударов.

Также посмотрите на unsubscribe $ и takeUntil (this.unsubscribe $) <- Используются для <strong>освобождения памяти после уничтожения компонента, для предотвращения утечек памяти .Это одна из лучших практик для этого.

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();
  }
}
0 голосов
/ 14 мая 2019

Я не англичанин, но помогу вам с аккуратным решением Javascript для вашей проблемы.Ваша проблема - пример из учебника обещаний Javascript .В Javascript a Promise - это объект, который может выдать одно значение в будущем .Я уверен, что вышеприведенное утверждение трудно понять, если вы никогда не слышали об этой концепции, но давайте подумаем со здравым смыслом.Promise в Javascript не отличается от обещания в реальной жизни.Если я вам что-то обещаю, может произойти следующее:

  • мое обещание уже выполнено в тот момент, когда я обещаю вам его, и в этом случае вы можете выполнить его
  • мое обещаниебудет выполнено некоторое время спустя в будущем
  • , в будущем будет очевидно, что мое обещание не было выполнено
  • мое обещание никогда не будет оценено

ЭтиИнтуитивные случаи имеют аналогичные случаи в программировании, когда вы работаете с обещаниями.

Пример для обещания:

new Promise(function(resolve, reject) {

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

});

Пример взят из https://javascript.info/promise-chaining

Как видите,конструктор Promise получает параметр function, который, в свою очередь, имеет параметр resolve и rejectresolve, и reject являются functions.resolve вызывается при выполнении Promise, тогда как reject вызывается при отклонении Promise.В нашем случае тело 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);
}

Цепочка ваших обещаний выглядит очень аккуратно, ее легко понять и поддерживать.Конечно, это скорее теоретический ответ, потому что вам нужно будет определить, что resolve, reject, promiseBody и requests выглядят одинаково в вашем конкретном случае, а также вам нужно будет справиться с возможнымиВ ситуации, когда на один из запросов по какой-то причине никогда не будет получен ответ, но как только вы поймете, что у вас есть очень элегантный способ продолжить и вам просто нужно выяснить детали, вы окажетесь на правильном пути.

Конечно, вы можете решить эту проблему традиционным способом, связав обратные вызовы, но это не очень элегантно.Вы также можете решить эту проблему с помощью генераторов Javascript, и в результате вы получите что-то более элегантное, чем решение старой школы обратного вызова ( hell ), например:

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

и гарантирующее передачуправильный request, когда это вызывается и всякий раз, когда выполняется обратный вызов, вызывается next(), передавая next request.

...