簡體   English   中英

Rxjs 使用 first() 和 timer()

[英]Rxjs using first() with timer()

我試圖每 10 秒調用一次服務,但也想只在第一次發射時執行一些代碼,這里的問題是第一個條目是重復的,這里是代碼:

ionViewWillEnter() {
  this.isLoading = true;
  const Obs = timer(0, 10000).pipe(
    switchMap(() => {
      return this.serviceName.someFunc();
    })
  );
  this.timerSub = Obs.subscribe();
  this.timerSub = Obs.pipe(first()).subscribe(() => {
    this.isLoading = false;
  });
}

我還注意到另一個問題,即使我在離開頁面時取消訂閱,該服務仍然每 10 秒調用一次,感謝任何幫助。

更新

我找到了一個解決方案,但它更像是一種解決方法,基本上我所做的就是在訂閱上設置一個 setTimeout:

this.timerSub = Obs.pipe(first()).subscribe(() => {
  this.isLoading = false;
});
setTimeout(() => {
  this.timerSub = Obs.subscribe();
}, 10000);

顯然,取消訂閱問題也得到了解決,盡管我希望能通過一些更優雅的解決方案獲得反饋,在此先感謝。

更好的解決方案:

在頂部創建變量firstSub

ionViewWillEnter() {
  this.isLoading = true;
  this.timerSub = timer(0, 10000).pipe(
    switchMap(() => {
      return this.serviceName.someFunc();
    })
  );
  this.firstSub = this.timerSub.pipe(first());
  this.firstSub.subscribe(() => {
    // only emit once(first)
    this.isLoading = false;
  });
}

在組件視圖銷毀之前取消訂閱。

ngOnDestroy(){
  this.timerSub.unsubscribe();
  this.firstSub.unsubscribe();
}

Nilesh Patel 提供的答案應該可以正常工作,但我仍然想添加此答案以分享您可能需要在應用程序中使用的一些小技巧和改進。

請看一下這個Stackblitz 演示

首先要注意的是,如果您正在使用timer運算符,並且您有興趣在它第一次發出時做某事,您可以檢查該運算符返回的值並查看它是否為0

timer(0, 10000).pipe(
  tap(currentTimer => {
    if (currentTimer === 0) {
      this.someFunctionToRunOnlyOnce();
    }
  }),
  // ...
);

要記住的第二件事是,您可以創建一個主題並像這樣使用takeUntil運算符,而不是將每個訂閱存儲在一個變量中(然后取消所有訂閱):

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

// ...

timer(0, 10000).pipe(
  // ... other operators
  takeUntil(this.unsubscribe$) // <-- like this
).subscribe();

// ...

ngOnDestroy() {
  this.unsubscribe$.next(); // <-- this will clean the streams
  this.unsubscribe$.unsubscribe(); // <-- this will clean the unsubscribe$ stream
}

另一個需要記住的非常小的事情是,您可以隨時“暫停”和“恢復” stream 而不會“破壞”它。 例如,您可以在離開頁面時暫停它,然后在用戶即將再次進入頁面時使用filter操作符再次恢復它:

private isInPage: boolean = true;

// ...

timer(0, 10000).pipe(
  filter(() => this.isInPage),
  // other operators ...
);

// ...

ionViewWillEnter() {
  this.isInPage = true;
}

ionViewWillLeave() {
  this.isInPage = false;
}

所以把所有這些放在一起它會是這樣的:

import { Component, OnInit } from "@angular/core";
import { NavController } from "@ionic/angular";
import { Observable, of, Subject, timer } from "rxjs";
import { delay, filter, switchMap, takeUntil, tap } from "rxjs/operators";

@Component({
  selector: "app-home",
  templateUrl: "./home.page.html",
  styleUrls: ["./home.page.scss"]
})
export class HomePage implements OnInit {
  private isInPage: boolean = true;
  private unsubscribe$: Subject<void> = new Subject<void>();

  constructor(private navCtrl: NavController) {}

  ngOnInit() {
    timer(0, 10000)
      .pipe(
        filter(() => this.isInPage),
        tap(currentTimer => {
          if (currentTimer === 0) {
            this.someFunctionToRunOnlyOnce();
          }
        }),
        switchMap(() => {
          return this.someAsynFunction();
        }),
        takeUntil(this.unsubscribe$)
      )
      .subscribe();
  }

  ionViewWillEnter() {
    this.isInPage = true;
  }

  ionViewWillLeave() {
    this.isInPage = false;
  }

  ngOnDestroy() {
    this.unsubscribe$.next();
    this.unsubscribe$.unsubscribe();
  }

  public openDetailsPage(): void {
    this.navCtrl.navigateForward("details");
  }

  private someAsynFunction(): Observable<number> {
    const randomNumber = Math.floor(Math.random() * 10000) + 1;

    console.log("==> Running someAsynFunction method");
    return of(randomNumber).pipe(delay(1000));
  }

  private someFunctionToRunOnlyOnce(): void {
    console.log("==> Running someAsynFunctionToRunOnlyOnce method");
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM