poll
In modern web applications, it's common to interact with APIs to fetch data, and sometimes, we need to continuously poll
these APIs to keep our data up-to-date. While there are various ways to implement polling mechanisms, leveraging the power of Reactive Programming can offer an elegant and efficient solution. Hence, the poll
operator, a handy tool in RxJS for simplifying the process of polling APIs comes to play.
The poll
operator is a custom operator built on top of RxJS, a powerful library for reactive programming in JavaScript. It allows developers to create observables that periodically emit data at specified intervals.
- Simplicity: It abstracts away the complexity of managing polling intervals and API requests.
- Flexibility: Developers can customize parameters, such as polling intervals and request payloads, to suit their application's needs.
- Reactivity: It seamlessly integrates with other RxJS operators, enabling developers to build reactive pipelines for handling data streams.
Usage
poll
operator is especially handy when fetching data periodically from an API endpoint. This is useful for scenarios where real-time updates are required but the server does not support push notifications.
By default, the poll
operator starts polling immediately. Please refer to section if you'd like to start polling after an action.
The poll
operator takes an options object containing essential parameters:
pollingFn
: A function to be executed periodically, that returns an Observable, Promise or a primitive value.interval
: The interval in milliseconds between each poll.paramsBuilder
: An optional function that builds parameters for the polling function.forceRefresh
: An optional Observable or Signal that forces an immediate execution of thepollingFn
function when emitted.initialValue
: An optional value as the initial value of the poll results. You can use it if you don't want to display aloading: true
state.delay
: An optional value if you'd like to delay the start of polling. By default, the polling starts immediately.
Example
Poll with different types of polling function
Polling function supports Promise, Observable, or primitive value.
import { poll } from 'ngx-lift';
import { ajax } from 'rxjs/ajax';
poll({
interval: 5000, // Poll every 5 second
pollingFn: () => Math.random(), // return a primitive value
}).subscribe(console.log);
poll({
interval: 5000,
pollingFn: () =>
new Promise((resolve) => {
setTimeout(() => { resolve(Math.random()); }, 200); // return a promise
}),
}).subscribe(console.log);
poll({
interval: 5000,
pollingFn: () => ajax('https://api.example.com/data'), // return an observable
}).subscribe(console.log);
Poll Until Condition is Met
Poll every 1 second until the output is greater than 8.
import { poll } from 'ngx-lift';
import { of, takeWhile, delay } from 'rxjs';
poll({
interval: 1000,
pollingFn: () => of(Math.random() * 10).pipe(delay(300)),
})
.pipe(takeWhile((state) => state.data === null || state.data <= 8, true))
.subscribe(console.log);
takeUntil, takeWhile
to end the polling as your need.Poll with initialValue
By default the poll initial result is {loading: true, error: null, data: null}
. It's convenient to display a spinner since loading is true. However, in some cases, you may not want to display a loading spinner. You can set initialValue
to any value you want.
import { poll } from 'ngx-lift';
import { of, delay } from 'rxjs';
poll({
interval: 1000,
pollingFn: () => of(Math.random() * 10).pipe(delay(300)),
initialValue: { loading: false, error: null, data: 0 }, // display 0 for initial value
}).subscribe(console.log);
Defer polling
By default, the polling immediately starts. We support delay
prop to delay the start of polling.
import { poll } from 'ngx-lift';
import { of } from 'rxjs';
poll({
interval: 1000,
pollingFn: () => of(Math.random() * 10),
delay: 3000, // delay 3s and start polling
}).subscribe(console.log);
Sometime you may want to start the polling after an action. poll
operator can work with other RxJS operators to achieve this.
const startPolling = new Subject<void>();
// start button click
function start() {
startPolling.next();
}
startPolling
.pipe(
switchMap(() => {
return poll({
interval: 1000,
pollingFn: () => of(Math.random() * 10).pipe(delay(300)),
}).pipe(takeWhile((state) => state.data === null || state.data <= 8, true));
}),
)
.subscribe(console.log);
Advanced Usage
- Force Refresh: Use the
forceRefresh
observable or signal to trigger the execution ofpollingFn
whenever necessary, such as in response to user actions. The emitted value fromforceRefresh
will be passed as the input toparamsBuilder
, if provided; otherwise, it goes directly topollingFn
. - Custom Parameters: Customize parameters by providing a
paramsBuilder
function, which is especially useful when theforceRefresh
value does not match the structure ofpollingFn
parameters. This function allows you to transform the input, ensuring it meets the specific requirements ofpollingFn
.
Advanced Example
The datagrid below will automatically refresh every 10 seconds. Additionally, whenever there's a change in the input, triggering filtering, sorting, or pagination, the API will be promptly called, accompanied by a spinner indicating loading.
import {ClarityModule, ClrDatagridStateInterface} from '@clr/angular';
import {AlertComponent, convertToHttpParams, dgState, PageContainerComponent} from 'clr-lift';
import {AsyncState, isEqual, poll} from 'ngx-lift';
export class PollComponent {
userService = inject(UserService);
private dgBS = new BehaviorSubject<ClrDatagridStateInterface | null>(null);
private dgState$ = this.dgBS.pipe(dgState(false));
usersState$ = poll({
interval: 10_000,
pollingFn: (params) => this.userService.getUsers({...params, results: 10, seed: 'abc'}),
paramsBuilder: (dgState: ClrDatagridStateInterface | null) => convertToHttpParams(dgState), // build params for getUsers
forceRefresh: this.dgState$, // datagrid filter, sort, pagination change will immediately force a refresh of the API call
});
}