i have the following code with Pusher:
Echo.private("channel").listen(".foobar", (e) => {
this.fetchData();
});
When there is an incoming request, I want data to be fetched again. But not on every Pusher event. I want the data to be fetched only once in 5 seconds.
I was thinking about a interval that is reset when there is a new incoming event, but how to achieve this?
Ok, first of all it's a Laravel Echo code. It may be using pusher behind the scenes, but saying it's a pusher code is slightly incorrect.
Then, if I understand you right, you need some kind of debounce function. For example lodash offers one.
const debouncedFunction = _.debounce(() => {
this.fetchData()
}, 5000);
Echo.private('channel').listen('.foobar', e => {
debounced()
})
debounce will create debounced version of function which you can then call, or even cancel, because it comes with cancel method.
debouncedFunction.cancel()
Related
I tried to set the interval for the function to delete it when the component will be destroyed but get this error. And can't find any solution for this.
My interval function:
<script>
export default {
data: () => ({
ordersInterval: null,
}),
async fetch() {
const data = await this.$axios.post(`${this.apiURL}orders`, {
per_page: this.$store.state.pagination.per_page,
page: this.$store.state.pagination.current_page,
})
this.orders = data.data.data
this.$store.dispatch('changePaginationData', {
paginationData: data.data.meta,
})
this.ordersInterval = setInterval(() => {
this.filterOrders()
}, 10000)
},
}
</script>
How can I fix this error?
ESlint is complaining and marking it as an error.
It's probably because fetch() needs to know when the fetching is done at some point, for helpers like $fetchState.pending especially.
I've used a setInterval in some of my code but it's called on an event. You could eventually have a watcher and call the setInterval when it's true (toggling it in your fetch() hook).
If you can, try to use websockets or a system a bit more flexible than polling.
Polling can be tricky to write also (with this), here is a good answer on how to to write it: https://stackoverflow.com/a/43335772/8816585
I am using angular 8.
There is one auto-complete input and if it's value changes I have to make API call and load new suggestions for this input.
//In Template
<autocomplate [suggestions]="suggestions" (filterChange)="filterChange($event)"></autocomplate>
//In Component
filterChange(e) {
console.log(e)
this.loadSubscriptions(e ? { 'filterItem.name': e } : {})
}
loadSubscriptions(params) {
if (this.suggestionsSubscriber) this.suggestionsSubscriber.unsubscribe()
this.suggestionsSubscriber = this.suggestionsService.loadData(params).subscribe(
data => this.suggestions = data
})
}
Everything works fine, but the problem is when user types fast application makes to many requests.
Can I somehow delay requests if user types fast? for example, while the user is typing don't make API calls on every change, and if the user stops typing then make API call.
Or if you have a better way to solve this problem, please share.
Use RXJS denounceTime operator. Simply chain it to your Observable.
Whenever debounceTime receives an event, it waits a designated amount of time to see if another event comes down the pipe. If it does, it restarts its timer. When enough time has passed without another event streaming in, it emits the latest event.
I would suggest you to use throttle or debounce. You can write your own implementation for those or use library such as lodash.
Debounce using latest Rxjs can be a work around. Please see below for implementation.
Angular and debounce
I also had a same problem, so i put my code inside setTimeout as below
filterChange(e) {
console.log(e)
setTimeout(()=>{
this.loadSubscriptions(e ? { 'filterItem.name': e } : {})
},2000);
}
Now if you type very fast then it will not call the loadSubscriptions at that time. it will call after 2 sec.
You can configure the time according to your choice.
I hope This will helps you.
I would like to query an API service every 15 seconds, so I can get data from a database and check whether something was changed. If there was a change, then my front end would update automatically because of how vue works.
while (true) {
setTimeout(function() {
QueryService.orders().then(response =>
this.orders = response.data
)
}, 15000)
}
My questions are:
Is this a good approach to solve such a problem at all?
What would be the best position in the code to place such a loop?
EDIT:
Using setInterval() seems to be the right way, but using a polling function with setInterval in the created() hook doesn't affect the data-table at all. It shows me "No data available":
data () {
return {
headers [
{ ... },
{ ... }
],
orders: []
}
created () {
setInterval(function() {
QueryService.orders().then(response => this.orders = response.data)
}, 15000)
}
Using the polling function without setInterval works and fills my data-table with data as usual:
created () {
QueryService.orders().then(response => this.orders = response.data)
}
For a simple and quick solution, I'd go with I'mOnlyVueman's answer. Here some example code I found from Vue.js polling using setINterval(). This example includes
pollData method initiated on created that dispatches a store action (which would call the API)
Canceling the poll as you navigate to another page using beforeDestroy
Code
data () {
return {
polling: null
}
},
methods: {
pollData () {
this.polling = setInterval(() => {
this.$store.dispatch('RETRIEVE_DATA_FROM_BACKEND')
}, 3000)
}
},
beforeDestroy () {
clearInterval(this.polling)
},
created () {
this.pollData()
}
But polling an API isn't very elegant and it doesn't scale well. You'll likely need to do something with Websockets, setting up your app to listen for events pushed from your API.
Here's info on Subscriptions in Vue-Apollo & GraphQL that Denis Tsoi mentioned.
Subscriptions are a GraphQL feature that allows the server to send
data to the clients when a specific event happens on the backend.
Subscriptions are usually implemented with WebSockets, where the
server holds a steady connection to the client. That is, the
Request-Response-Cycle that we used for all previous interactions with
the API is not used for subscriptions. Instead, the client initially
opens up a steady connection to the server by specifying which event
it is interested in. Every time this particular event happens, the
server uses the connection to push the data that’s related to the
event to the client.
A loop like this would go in the component's script within a mounted () lifecycle hook.
That would mean once the component loads your loop would trigger. For detailed guidance on this technique the Vue docs are a good first stop, as well as this article.
I am trying to implement autocomplete using Handontable (Angular) by fetching data from the server as the user types.
I notice the API calls are made every time the user input changes but I would like to limit the number of API calls by waiting 1 second for the user to stop typing before making the call.
I have done this in the past when I controlled the event by using debounceTime but not sure how to implement that here.
...
column.source = function(query, callback) {
$component.dataService.getValidValues().subscribe(
arg => {
callback(arg);
},
err => {
...
}
);
}
...
Adding debounceTime(1000) here doesn't prevent multiple calls from happening.
$component.dataService.getValidValues().debounceTime(1000).subscribe(...)
As already explained by others you need to debounce the input. In your case this would be the invocation of the function.
One way to achieve that is using a subject that you create somewhere in your code:
const sourceRequest = new Subject();
sourceRequest.debounceTime(1000).subscribe(callback => {...});
The code you currently have inside function(query, callback) { goes into subscribe. The column definition is then changed to this:
column.source = function(query, callback) {
sourceRequest.next(callback);
}
Assuming that .getValidValues() is the function that makes the request to the remote server, you are debouncing the stream of events coming from this function. You want to debounce the stream of events coming from the user input, which limits the number of calls to .getValidValues().
Try something like this
$component.dataService.debounceTime(1000).getValidValues().subscribe(...)
The only thing I can think of is for you to create a standard Javascript debounce function that wraps the function you are assigning to column.source, kind of like this:
https://stackblitz.com/edit/angular-mq9jyv?file=src%2Fapp%2Fapp.component.ts
So I have a checkbox that is tied to a model:
<mat-checkbox [(ngModel)]="element[column]" (click)="updateRow(element, $event)"></mat-checkbox>
The click event is supposed to call the server and perform a write in the db.
So, for this, I thought that using concatMap was enough since it waits before making the next call to avoid exhausting the database.
updateRow(row, $event): void {
Observable.from([{ row: row }])
.concatMap(i => this._definitionService.updateDefinition(i) //async work
.subscribe(result => console.log('row updated'), error => {
this.snack(error);
});
}
The problem is that it does not wait since every event change is a new call, and performing too many changes exhaust the database.
What would be the solution for this? I thought about tying the events using fromEvent like this:
Observable.fromEvent($event.target, 'click')
.subscribe(i => {
console.log(i);
});
But, it only works when clicking twice on the checkbox.
What would be the correct approach?
Thanks
The reason why it's calling many requests is because every time you click the element you call updateRow that creates new instance of Observable.from. So concatMap won't help you because you when you make 10 clicks it subscribes to 10 different Observables with 10 different concatMaps.
Probably the easiest way to achieve what you want is creating an intermediate Subject and pushing item into it every time you call updateRow:
const s = new Subject();
s.concatMap(i => this._definitionService.updateDefinition(i))
.subscribe(
result => console.log('row updated'),
error => this.snack(error),
);
...
updateRow(row, $event): void {
s.next({ row });
}
Now you'll have just one subscription and pushing multiple item into Subject s (with next()) will execute them in order one after another.