I have this scenario, where the database is updated regularly, for example say stock market prices (I do not need immediate updates a poll every 2 minutes is good enough).
Using short intervals like 2 or 5 seconds
will lead to performance issues so I am choosing 2 minutes
as my threshold for polling.
I want to implement a polling mechanism to update the values at the set interval.
Short and long polling
I know it can be done with push based systems, but I want to achieve this with pull/poll based system.
Below it the minimal reproducible code and working stackblitz for reference:
@Component({
selector: 'app-root',
imports: [CommonModule],
template: `
<div>{{rxResource.value() | json}}</div>
<hr/>
<div>{{resource.value() | json}}</div>
`,
})
export class App {
http = inject(HttpClient);
serviceIdSignal: WritableSignal<number> = signal(1);
rxResource = rxResource({
request: () => this.serviceIdSignal(),
loader: ({ request: id }) => {
return this.http.get(`/${id}`);
},
});
resource = resource({
request: () => this.serviceIdSignal(),
loader: ({ request: id }) => {
return fetch(`/${id}`).then(
(res: any) => res.json()
);
},
});
}
The above code does not react/poll, my input signal stays the same, but I need to poll using resource API.
Stackblitz Demo
I have this scenario, where the database is updated regularly, for example say stock market prices (I do not need immediate updates a poll every 2 minutes is good enough).
Using short intervals like 2 or 5 seconds
will lead to performance issues so I am choosing 2 minutes
as my threshold for polling.
I want to implement a polling mechanism to update the values at the set interval.
Short and long polling
I know it can be done with push based systems, but I want to achieve this with pull/poll based system.
Below it the minimal reproducible code and working stackblitz for reference:
@Component({
selector: 'app-root',
imports: [CommonModule],
template: `
<div>{{rxResource.value() | json}}</div>
<hr/>
<div>{{resource.value() | json}}</div>
`,
})
export class App {
http = inject(HttpClient);
serviceIdSignal: WritableSignal<number> = signal(1);
rxResource = rxResource({
request: () => this.serviceIdSignal(),
loader: ({ request: id }) => {
return this.http.get(`https://jsonplaceholder.typicode/todos/${id}`);
},
});
resource = resource({
request: () => this.serviceIdSignal(),
loader: ({ request: id }) => {
return fetch(`https://jsonplaceholder.typicode/todos/${id}`).then(
(res: any) => res.json()
);
},
});
}
The above code does not react/poll, my input signal stays the same, but I need to poll using resource API.
Stackblitz Demo
Share Improve this question edited 2 days ago D M 7,1794 gold badges18 silver badges37 bronze badges asked 2 days ago Naren MuraliNaren Murali 57.4k5 gold badges41 silver badges71 bronze badges 4- Polling is absolutely the wrong pattern to use in this scenario. Use websockets/grpc to keep the client/server in close synchronicity. – Rory McCrossan Commented 2 days ago
- @RoryMcCrossan I have mentioned that I do not need it, I want pull based system, that too long polling – Naren Murali Commented 2 days ago
- Then I'd suggest changing that requirement as polling will cause your system to have performance issues given a relatively low load. – Rory McCrossan Commented 2 days ago
- @RoryMcCrossan If the polling is for a long interval, there will not be any performance issues. – Naren Murali Commented 2 days ago
1 Answer
Reset to default 1You can use the rxjs operator interval
to trigger for a fixed number of seconds, this will create the polling trigger.
The interval is an observable, we need to convert it to a signal so that it can be used as an input for resource
APIs, for this we use toSignal
to convert it.
// we use rxjs interval to achieve polling of apis, enter the time in milliseconds
// long polling -> longer time
// short polling -> shorter time
// every 2 minutes
pollSignal = toSignal(interval(120000));
Then we pass this as an input to our resource
APIs and polling should happen as expected.
rxResource = rxResource({
request: () => ({ id: this.serviceIdSignal(), poll: this.pollSignal() }),
loader: ({ request: { id } }) => {
return this.http.get(`https://jsonplaceholder.typicode/todos/${id}`);
},
});
resource = resource({
request: () => ({ id: this.serviceIdSignal(), poll: this.pollSignal() }),
loader: ({ request: { id } }) => {
return fetch(`https://jsonplaceholder.typicode/todos/${id}`).then(
(res: any) => res.json()
);
},
});
We can add a small loader using ResourceStatus
to indicate a refresh.
<div>
@if(![rs.Loading, rs.Reloading].includes(rxResource.status())) {
{{rxResource.value() | json}}
} @else{
Loading...
}
</div>
<hr/>
<div>
@if(![rs.Loading, rs.Reloading].includes(resource.status())) {
{{resource.value() | json}}
} @else{
Loading...
}
</div>
Full Code:
import {
Component,
WritableSignal,
signal,
resource,
inject,
ResourceStatus,
} from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { HttpClient, provideHttpClient } from '@angular/common/http';
import { CommonModule } from '@angular/common';
import { rxResource, toSignal } from '@angular/core/rxjs-interop';
import { interval } from 'rxjs';
@Component({
selector: 'app-root',
imports: [CommonModule],
template: `
<div>
@if(![rs.Loading, rs.Reloading].includes(rxResource.status())) {
{{rxResource.value() | json}}
} @else{
Loading...
}
</div>
<hr/>
<div>
@if(![rs.Loading, rs.Reloading].includes(resource.status())) {
{{resource.value() | json}}
} @else{
Loading...
}
</div>
`,
})
export class App {
rs = ResourceStatus;
http = inject(HttpClient);
serviceIdSignal: WritableSignal<number> = signal(1);
// we use rxjs interval to achieve polling of apis, enter the time in milliseconds
// long polling -> longer time
// short polling -> shorter time
// every 2 minutes
pollSignal = toSignal(interval(120000));
rxResource = rxResource({
request: () => ({ id: this.serviceIdSignal(), poll: this.pollSignal() }),
loader: ({ request: { id } }) => {
return this.http.get(`https://jsonplaceholder.typicode/todos/${id}`);
},
});
resource = resource({
request: () => ({ id: this.serviceIdSignal(), poll: this.pollSignal() }),
loader: ({ request: { id } }) => {
return fetch(`https://jsonplaceholder.typicode/todos/${id}`).then(
(res: any) => res.json()
);
},
});
}
bootstrapApplication(App, {
providers: [provideHttpClient()],
});