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

很明显,取消订阅的问题也得到了解决,尽管我很感激能提供一些更优雅的解决方案,提前谢谢。

Nilesh Patel提供的答案应该很好,但我仍然想添加这个答案,以分享您可能需要在应用程序中使用的一些小技巧和改进。

请看一下Stacklitz演示

首先要注意的是,如果您正在使用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
}

另一件需要记住的非常小的事情是,你可以";暂停";以及";简历";无论何时你想要的流没有";销毁";例如,您可以在离开页面时暂停它,然后在用户即将再次进入页面时使用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");
}
}

更好的解决方案:

在顶部创建变量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();
}

最新更新