Lodash throttle firing multiple times - javascript

I am using lodash throttle like this
const throttledFetch = _.throttle(fetch, 10000, { 'leading': false });
I need to trigger this upon a certain notification event I am getting from a ws. So the idea was, if I get 10 notifications at almost the same time, to have the fetch function fire only once at the wait of 10 seconds.
Instead, what is happening is that the fetch functions gets fired 10 times after the 10 second delay.
How can I fix this? I could use any other methods.
Any suggestion is welcome

The throttled function should remain the same between re-renderings, meaning we have to use React's UseCallback function. This would have worked if you changed:
const throttledFetch = _.throttle(fetch, 10000, { 'leading': false });
To
const throttledFetch = useCallback(_.throttle(fetch, 10000, { 'leading': false }));

Keep a counter for the invocation and check it for invoking only once.

if we are going to call the fetch function only once, for all the N requests at same time, we should be using debounce instead of throttle. That is the best way to respond to user interaction / some event from the Web socket listener.
The response should be faster, and also it should not be called frequently. In order to full fill the above requirement, I would be going with _debounce.
As answered by Tuxedo Joe in the above answer, we can go with useCallback approach, since the reference is going to stay the same between re-renders.
const asyncFetch = useCallback(_.debounce(fetch, 10000, { 'leading': false }));

Related

React Performance Issues in Firefox?

I'm experiencing some performance issues with a react application that I developed. These issues specifically (or most notably) occur with Firefox (both FF developer 77.0b7 and FF 76.0.1).
When using this application in Firefox, CPU usage gets extremely high, and my fans start spinning up to very high speeds. I get about 15-19fps in firefox according to the performance tools in FF. I get roughly 60fps in Chrome and Safari.
These issues occur when I begin typing into the input field, and get worse as the input gets longer (which makes sense)
The application is available here:
https://text-to-aura-generator.netlify.app/
Source code available here: https://github.com/paalwilliams/Text-to-Aura/tree/master/src
I'm almost certain that this is something I'm doing incorrectly, or that I've written the code inefficiently, but that isn't necessarily supported by the stark performance difference between browsers. Is chrome just that much better and handling react/constant rerenders?
I know that this is a broad question, but I honestly don't understand what is happening here, or necessarily how to troubleshoot it beyond the developer tools. Any input or thoughts would be greatly appreciated.
The problem is your application is rendering too fast. In your particular case, there a few ways to improve that.
Every time you update the state, React needs to re-render your application, so updating the state within a loop is usually a bad idea.
Also, you are using useState 3 times, but only colors should be there, as App actually needs to re-render to reflect the changes there. The other two pieces of state (text and hex) are only being used to pass data from the handleChange to the callback inside useEffect.
You can restructure your code to:
Avoid updating the state within a loop.
Use a simple variable instead of state.
Use useCallback to define a function with that logic that is not re-created on each render, as that forces TextInput to re-render as well.
Throttle this callback using something like this:
import { useCallback, useEffect, useRef } from 'react';
export function useThrottledCallback<A extends any[]>(
callback: (...args: A) => void,
delay: number,
deps?: readonly any[],
): (...args: A) => void {
const timeoutRef = useRef<number>();
const callbackRef = useRef(callback);
const lastCalledRef = useRef(0);
// Remember the latest callback:
//
// Without this, if you change the callback, when setTimeout kicks in, it
// will still call your old callback.
//
// If you add `callback` to useCallback's deps, it will also update, but it
// might be called twice if the timeout had already been set.
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
// Clear timeout if the components is unmounted or the delay changes:
useEffect(() => window.clearTimeout(timeoutRef.current), [delay]);
return useCallback((...args: A) => {
// Clear previous timer:
window.clearTimeout(timeoutRef.current);
function invoke() {
callbackRef.current(...args);
lastCalledRef.current = Date.now();
}
// Calculate elapsed time:
const elapsed = Date.now() - lastCalledRef.current;
if (elapsed >= delay) {
// If already waited enough, call callback:
invoke();
} else {
// Otherwise, we need to wait a bit more:
timeoutRef.current = window.setTimeout(invoke, delay - elapsed);
}
}, deps);
}
If the reason to use useEffect is that you were not seeing the right values when updating colors, try using the version of setState that takes a callback rather then the new value, so instead of:
setColors([...colors, newColor]);
You would have:
setColors(prevColors => ([...prevColors , newColor]));
The most common performance issues with react come from setting the state too many times since you're constantly re rendering the page and the elements within it.

Angular, to many API calls on input 'on-change'

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.

Angular4 - do I need to unsubscribe from setTimeout calls?

In a few places I have needed to call setTimeout, eg:
setTimeout(() => this.isSaving[index] = false, 500);
When my component is destroyed, will that timeout continue to emit? In other words do I need to capture the returned observable, like this:
this.subTimeout = setTimeout(() => this.isSaving[index] = false, 500);
and then unsub in my destroy hook:
ngOnDestroy() {
this.subTimeout.unsubscribe();
}
This gets laborious if I have to initiate several setTimeouts in my component. Is there an easier way to destroy them all? Like maybe with takeUntil(destroy$)?
do I need to unsubscribe from setTimeout calls?...When my component is destroyed, will that timeout continue to emit?
You don't need to worry about the callback executing repeatedly. setTimeout executes just once then it's dead. It's generally good practice to account for the possibility that the time will run out and the callback execute only after the component has been destroyed though. In my own applications, I throw in an array all subscriptions that need to be undone, and I have a standard batch unsubscribe job in ngOnDestroy. The same can be done with your timeouts:
// component property
timeOutIDs:number[] = [];
...
// triggering a timeout and capturing the id
this.timeOutIDs.push(
setTimeout(() => this.isSaving[index] = false, 500)
);
...
// inside ngOnDestroy
this.timeoutIDs.forEach(id => clearTimeout(id));
With this approach you won't need multiple variables to store different timeout ids, and you can be sure that all your timeouts will be cleared properly if you always push the return value of setTimeout in your ids array.
Additional note: You should always cancel setInterval calls and always unsubscribe from open-ended subscriptions though.
It depends on what's going on inside setTimeout callbacks, but generally they should be unsubscribed. There's no way how they could be magically be unsubscribed. Their callbacks will be fired any way and may cause errors or undesirable side effects.
It is a good practice to make timeout be assigned somewhere, at least for the purpose of testing. The thing that is supposed to be done in ngOnDestroy, this.subTimeout.unsubscribe() presumes that a timeout is performed through RxJS:
this.subTimeout = Observable.timer(500).subscribe(() => {
this.isSaving[index] = false;
});
The way it can be improved depends on what purpose these timeouts serve.

Prevent JavaScript Timer Re-entry

My ASP.NET MVC page uses JavaScript/jQuery to poll my database every second.
This is working but I want to make sure that, if there is a delay, my timer handler won't get called again before it has returned.
In there any trick to this other than storing the timer ID in a global variable, clearing the timer in my handler, and restarting it when my handler is done.
NOTE: I realize every second seems frequent but this code is polling my server after submitting a credit card payment. Normally, it will only run for a second or so, and I don't want the user to wait any longer than necessary.
Polling every second? That's quite heavy!
That aside, you won't have this issue when setTimeout is used instead of setInterval. The latter ensures that a piece of code is run x times given a interval, while the former ensures that there's a delay of at least x milliseconds.
function some_poller() {
$.ajax({
url: '/some_page',
success: function() {
setTimeout(some_poller, 1000);
},
error: function() { // Also retry when the request fails
setTimeout(some_poller, 1000);
}
});
}
// Init somewhere
some_poller();
Not really, although I wouldn't recommend using a global variable. Stick it inside some function.
But are you really sure you need to poll every second? That's an extremely chatty interface.
In my personal experience a "global", (inside of the root function), variable works very well in this instance so that you can control when to clear and restart. If the response is really as quick as you say, this shouldn't cause too much overhead, (clearing/resetting), and will allow to account for these type of situations.

Is it OK to call clearInterval inside a setInterval handler?

I have a piece of Javascript that checks for a condition (via an AJAX call) every n seconds. If that condition is true, it stops checking. I have implemented it in the following way:
var stopTimer;
var timerId = setInterval(function() {
/* Make Ajax Calls and set stopTimer */
if (stopTimer) {
clearInterval(timerId);
}
}, 10000);
However, I find erratic behaviour: Works sometimes, but at other times, it keeps checking forever. I have checked that (as much as is possible) there is no error in any part of the code.
I am therefore suspecting that calling clearInterval inside a setInterval handler might be the culprit. Is that right? Is it OK to call clearInterval inside a setInterval handler?
Thank you for your attention
It's safe. The issue is probably to do with stopTimer not being set as you expect.
I don't think there will be any issue with your code unless the AJAX function is erroneous. You have to take care of the success and error callbacks of the AJAX function so that there won't be any issue with the loop not being stopped.
Also I think you are constantly polling the server for a response and then doing the appropriate action. You can use Reverse AJAX to do this kind of process.
Make sure you're not inadvertently re-using the same timer name elsewhere in your code which would result in you always stopping the second timer to be defined.
Either give the timer a unique name, or scope it to a function
var timerForAjax = setInterval(function() {
/* Make Ajax Calls and set stopTimer */
if (stopTimer)
{
clearInterval(timerForAjax);
}
}, 10000);
I was careless enough to call my timer interval and didn't realize I was creating two timers in the same scope both called interval. Blamed iOS8 for about an hour until I realized that that was nothing to do with it.

Categories