简体   繁体   English

Angular 5,rxjs-仅在运行另一个进程之前等待可观察对象完成

[英]Angular 5, rxjs- wait for observable to finish only if it is in a middle of a run before running another process

I have a timer in my main component of 3 seconds.我的主要组件中有一个 3 秒的计时器。 inside the timer I perform http call-在计时器内我执行 http 调用-

    constructor(){
          this.timer = timer(3000, 3000);
          this.timerObservable = this.timer.subscribe(x => {
              this.http.get(URL).subscribe(()=>{
              //DO SOMETHING
              });
          });

     }

In another component I have a button that suppose to perform a different http call, pressing on the button invoke the sumbit function-在另一个组件中,我有一个按钮,假设执行不同的 http 调用,按下按钮调用 sumbit 函数 -

        submit(){
            this.http.get("/sumbitForm").subscribe(()=> {
              //DO SOMETHING
             })
         }

When a user clicks on the button, if the timer is in process (the http inside of it was called and not resolved yet) I want to wait before I perform the http call on the button until it resolved, but if the timer is not in process (the time from the previous call did not passed yet) I want to execute it immediately.当用户单击按钮时,如果计时器正在处理中(其中的 http 被调用但尚未解析),我想在对按钮执行 http 调用之前等待,直到它解析为止,但如果计时器没有在处理中(上次调用的时间尚未过去)我想立即执行它。

I think that forkJoin and concat is not relevant here (this is a timer and not a 'regular' subscription that I want to wait to its execution either way) and I could not find a pretty way to do it, any idea?我认为 forkJoin 和 concat 在这里不相关(这是一个计时器,而不是我想以任何方式等待执行的“常规”订阅)并且我找不到一个很好的方法来做到这一点,知道吗?

You need to share some information between your two components, ie when the polling request is in progress and when it isn't.您需要在两个组件之间共享一些信息,即轮询请求何时在进行中,何时不在。 You should use a Service for this.您应该为此使用服务。 It's also always good practice to move your http request logic to a Service instead of using the HttpClient directly in the component.将您的 http 请求逻辑移动到服务而不是直接在组件中使用 HttpClient 也是一种很好的做法。 This allows you to do your general error handling in one place.这允许您在一个地方进行一般错误处理。 Let's call this Service ApiService.我们将此服务称为 ApiService。

ApiService服务

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject, interval } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';

@Injectable({ providedIn: 'root' })
export class ApiService {
  // Use a BehaviorSubject to notify listeners about ongoing polling requests.
  // A BahaviorSubject always has a current value that late subscribers will 
  // receive immediately upon subscribing
  private pollRequestInProgress = new BehaviorSubject<boolean>(false);
  // Share this BehaviorSubject as an Observable
  public pollRequestInProgress$ = pollRequestInProgress.asObservable();

  constructor(private http: HttpClient)

  doPoll(url: string): Observable<any> {
    return interval(3000).pipe( // interval(3000) is equivalent to timer(3000, 3000)
      tap(_ => pollRequestInProgress.next(true)), // notify that a poll request is about to happen
      switchMap(_ => this.http.get(url)), // do your poll request
      tap(_ => pollRequestInProgress.next(false)) // notify that your poll request ended
    );
  }
}

MainComponent主组件

This is the Component where you want to start your polling from.这是您要从中开始轮询的组件。

private destroy$: Subject<void> = new Subject<void>();

constructor(private apiService: ApiService) {}

// move logic to ngOnInit instead of constructor
ngOnInit() {
  // subscribe and thereby start the polling
  this.apiService.doPoll(URL).pipe(takeUntil(this.destroy$))
    .subscribe(pollResponse => {
      //DO SOMETHING
    });
}

ngOnDestroy() {
  // unsubscribe when the Component gets destroyed.
  this.destroy$.next();
  this.destroy$.complete();
}

FeatureComponent特征组件

This is the Component where you want to perform a http request when you click a button.这是当您单击按钮时要在其中执行 http 请求的组件。

constructor(private http: HttpClient, private apiService: apiService) {}

submit() {
  // Listen to whether a polling request is currently in progress.
  // We will immediately receive a value upon subscribing here, because we used 
  // a BehaviorSubject as the underlying source.
  this.apiService.pollRequestInProgress$.pipe(
    // Emit the first item that tells you that no polling request is in progress.
    // i.e. let the first 'requestInProgress === false' through but not any 
    // other items before or after.
    first(requestInProgress => !requestInProgress),
    // If no polling request is in progress, switch to the http request you want 
    // to perform
    switchMap(this.http.get("/sumbitForm")) // <-- consider moving this http.get to your ApiService aswell
  ).subscribe(httpResponse => {
    // you've got your http response here
  });
  // you don't have to unsubscribe here as first and http.get both complete 
  // and thus unsubscribe automatically
}

Check out a simple example of the code logic above here: https://stackblitz.com/edit/rxjs-t4hjcr在此处查看上面代码逻辑的简单示例: https : //stackblitz.com/edit/rxjs-t4hjcr

You can use Angular Subject您可以使用角度主题

import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class CallService {
    private subject = new Subject<any>();

    timerCompleted() {
        this.subject.next();
    }

    checkTimer(): Observable<any> {
        return this.subject.asObservable();
    }
}

The app component uses the call service to subscribe to timer complete and make them available to the app component template.应用程序组件使用调用服务订阅计时器完成并使它们可用于应用程序组件模板。

import { Component, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';

import { CallService } from './_services/index';

@Component({
    selector: 'app',
    templateUrl: 'app.component.html'
})

export class AppComponent implements OnDestroy {

    subscription: Subscription;

    constructor(private callService: CallService) {

        this.subscription = this.callService.checkTimer().subscribe(() => { 
           // call your api after timer complete
        });
    }

    ngOnDestroy() {
        // unsubscribe to ensure no memory leaks
        this.subscription.unsubscribe();
    }
}

add below code in your timer在您的计时器中添加以下代码

this.timer = timer(3000, 3000);
        this.timerObservable = this.timer.subscribe(x => {
           this.http.get(URL).subscribe(()=>{
               this.callService.timerCompleted();
           });
        });

For more reference you can check http://jasonwatmore.com/post/2018/06/25/angular-6-communicating-between-components-with-observable-subject如需更多参考,您可以查看http://jasonwatmore.com/post/2018/06/25/angular-6-communicating-between-components-with-observable-subject

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM