Issue
i'm trying to call a service every 10seconds but also want to execute some code only on the first emission, the problem here is that the first entry is duplicated, here is the code :
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;
});
}
i also noticed another problem which is that even though i unsubscribe when i leave the page, the service is still being called every 10 seconds, any help is appreciated.
UPDATE
i found a solution, but it's more of a workaround, basically what i did was put a setTimeout on the subscription :
this.timerSub = Obs.pipe(first()).subscribe(() => {
this.isLoading = false;
});
setTimeout(() => {
this.timerSub = Obs.subscribe();
}, 10000);
and apparently the unsubscribe problem was solved as well, although i would appreciate the feedback with some more elegant solutions, thanks in advance.
Solution
The answer provided by Nilesh Patel should work fine, but I still wanted to add this answer to share some minor tips and improvements that you may need to use in your app.
Please take a look at this Stackblitz demo.
The first thing to notice is that if you're using the timer
operator and you're interested in doing something the first time it emits, you can check the value returned by that operator and see if it's 0
:
timer(0, 10000).pipe(
tap(currentTimer => {
if (currentTimer === 0) {
this.someFunctionToRunOnlyOnce();
}
}),
// ...
);
The second thing to keep in mind is that instead of storing every subscription in a variable (to then unsubscribe from all of them) you can create a subject and use the takeUntil
operator like this:
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
}
And another very minor thing to keep in mind is that you can "pause" and "resume" the stream whenever you want without "destroying" it. For example, you can pause it when leaving the page and then resume it again when the user is about to enter to the page again by using the filter
operator:
private isInPage: boolean = true;
// ...
timer(0, 10000).pipe(
filter(() => this.isInPage),
// other operators ...
);
// ...
ionViewWillEnter() {
this.isInPage = true;
}
ionViewWillLeave() {
this.isInPage = false;
}
So putting all that together it'd be something like this:
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");
}
}
Answered By - sebaferreras
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.