Stop/Restart Requests When User Is Inactive using Angular, RxJS, and Page Visibility API

October 5, 2022 • , , • Published By • 4 minute read

It’s common for websites or apps to request fresh data on a set interval. What if the user isn’t looking at your website or app anymore? What if they minimized the browser or switched to a different tab or app? Let’s learn how to stop and restart network requests using the Page Visibility API!

Table of Contents

Setup

We want to create a service that will be responsible for getting a random quote from the Quotable API. We need to import some things we’ll use within the service. Let’s get those set up now.

import { HttpClient } from '@angular/common/http';

import { BehaviorSubject, fromEvent, Observable, Subject, timer } from 'rxjs';
import { map, repeatWhen, switchMap, takeUntil, tap } from 'rxjs/operators';

Let’s define an interface for what the Quote will look like.

interface Quote {
  _id: string;
  authorSlug: string;
  content: string;
  dateAdded: string;
  dateModified: string;
  length: number;
  tags: string[];
}

Let’s set up the variables we’ll be using as well.

private readonly _restartTimer = new Subject<void>();
private readonly _stopTimer = new Subject<void>();

private readonly _randomQuote = new BehaviorSubject<Quote | null>(null);

readonly randomQuote$ = this._randomQuote.asObservable();

To display the actual quote, we can use the async pipe to subscribe to the observable we created called randomQuote$.

<blockquote *ngIf="randomQuote$ | async as randomQuote">
  {{ randomQuote.content }}
</blockquote>

Get Random Quote

We want to use the HttpClient to make HTTP requests to the Quotable API. Within the service’s constructor, pass in the following:

constructor(private http: HttpClient) {}

Now let’s create a function called getRandomQuote. It will make a GET request to the Quotable API and return a random quote. The returned quote will be passed to the randomQuote BehaviorSubject.

private getRandomQuote(): Observable<Quote> {
  return this.http.get<Quote>('https://api.quotable.io/random').pipe(
    tap(quote => this._randomQuote.next(quote))
  );
}

Get Random Quote Every Minute

Now that we can get a random quote, let’s try getting one every minute! We’ll need the RxJS timer function to run every minute and the RxJS switchMap operator to switch to the observable that gets the random quote after a minute has passed.

Create a function called createTimer. It will create the timer and get a random quote every minute. The time is in milliseconds so 60,000 milliseconds equals 1 minute.

private createTimer(): void {
  timer(0, 60000).pipe(
    switchMap(() => this.getRandomQuote())
  ).subscribe();
}

Stop/Restart Timer

We need a way to stop and restart the timer. Let’s create two functions for that called restartTimer and stopTimer. Both will call next on their respective Subjects to trigger the action.

private restartTimer(): void {
  this._restartTimer.next();
}

private stopTimer(): void {
  this._stopTimer.next();
}

Now let’s update our createTimer function and add in a couple more operators. The takeUntil operator will listen for the stopTimer Subject to trigger. If it is triggered, it will stop the timer. The repeatWhen operator will listen for the restartTimer Subject to trigger. If it is triggered, it will restart the timer.

private createTimer(): void {
  timer(0, 60000).pipe(
    takeUntil(this._stopTimer),
    repeatWhen(() => this._restartTimer),
    switchMap(() => this.getRandomQuote())
  ).subscribe();
}

Page Visibility API

We’re getting a random quote every minute, but what if the user isn’t looking at our website or app anymore? What if they minimized the browser or switched to a different tab or app? Let’s stop getting a random quote every minute until the user comes back.

There is a helpful event called visibilitychange that we can listen for. It provides a visibilityState property that will tell you whether the page content is visible or hidden to the user.

Create a function called onVisibilityChange that will listen for the visibilitychange event. If the visibility state is visible, we can restart the timer. If not, stop the timer.

private onVisibilityChange(): void {
  fromEvent(document, 'visibilitychange').pipe(
    tap((event: Event) => {
      const state = (event.target as Document).visibilityState;

     if (state === 'visible') {
        this.restartTimer();
      } else {
        this.stopTimer();
      }
    })
  ).subscribe();
}

All that’s left to do is call the createTimer and onVisibilityChange functions in the constructor.

constructor(private http: HttpClient) {
  this.createTimer();
  this.onVisibilityChange();
}

Congrats on saving server resources when the user isn’t actively using your website or app!

Related Articles
About the Author

Front End Developer

https://nightwolf.dev