🤓 A Brief Overview

As developers, there are times when we want to provide real-time updates for specific features in our application. A typical example might be user notifications, alerts, or to verify that the current user session hasn’t expired.

Unfortunately, not all servers provide push-based notifications such as web-sockets, so we’ll most likely create a polling system that goes and fetches the result from a REST endpoint once in a given cycle.

With tabbed browsing, there is a reasonable chance that any given webpage is in the background and thus not visible to the user. In such a case, when the user isn’t really engaging with the application, there’s no reason to keep polling and cause the server to perform redundant operations.

We should always strive to interrupt the server as little as possible so that it is able serve other users more efficiently. Let’s see how we can do this.

👁 Page Visibility API

The Page Visibility API lets your application know when a page is visible to the user. This basic information enables the creation of web pages that behave differently when they’re not being viewed.

Whenever the user minimizes the window or switches to another tab, the API emits a visibilitychange event to let listeners know the state of the page has changed. It does so also when the page becomes visible again. We can detect the event, and perform actions or alter the behavior of existing ones accordingly.

🦊 Use Case Example (Polling)

For our example, we’ll create a notification service that polls notifications from the server. (We’ll use Angular, but it will work with any other implementation)

Let’s create the infinite poll operator with RxJS, combining the timer and concatMap operators. This is an elegant way to poll from the server, you can read more about why here.

Building your own operator is as simple as writing a function which takes a source observable as an input and returns an output stream.

In our case, we use the timer observable that takes a time period and an initial delay (which defaults to zero as we usually want to start the polling immediately) and concat the source observable. This will give us infinite polling functionality.

Next, we’ll create one last custom RxJS operator that leverages the Page Visibility API together with two built-in operators, in order to achieve the desired functionality:

The visibilitychange$ observable uses the fromEvent observable, which adds a visibilitychange event listener to the document object. Since we want to create an event listener only once, we employ the shareReplay operator.

Then, we create two observables variations based on the visibilitychange$ observable, one for when the page is visible and the another for when it’s hidden.

The final step is to chain the original source with two built-in operators that do all the work for us:

The takeUntil operator takes an observable. When it emits, it will automatically unsubscribe from the source observable. In our case, when the user switches tab pageHidden$ will emit, and cause a termination of the notifications polling.

The repeatWhen operator does the opposite; It takes a function which returns an observable. When this observable emits it resubscribes to the source observable. In our case, when the user returns to our application , it will re-subscribe to the notifications polling. It’s as simple as that 😀

A bit of advice: consider adding a retry functionality in case of error.

🚒 Hot Update

Starting from RxJS v6.5, we can make this example even shorter and use the partition observable creation:

😍 🚀 Have You Tried Akita Yet?

One of the leading state management libraries, Akita has been used in countless production environments. It’s constantly developing and improving.

Whether it’s entities arriving from the server or UI state data, Akita has custom-built stores, powerful tools, and tailor-made plugins, which help you manage the data and negate the need for massive amounts of boilerplate code. We/I highly recommend you try it out.

Follow me on Medium or Twitter to read more about Angular, Akita and JS!