I have the following function that is being called from a keyPress listener on an input text field. The _.debounce() is working correctly, except instead of only firing the function 1 time after the time period, it is firing as many times as the keyPress event happened.
console.log("Pre Debounce");
var debounced = _.debounce(function() {
console.log("Field updated!");
}, 2000);
debounced();
Is there a way to limit the _.debounce function to only fire 1 time after the time period?
Possibly you are constructing the debounce function each time inside the event. If that's your case then take your debounce function outside the event response code. As far as I know the debounced function should be generated only one time and then be called multiple times.
Another thing that could go weird is when you are making an async call (ex. with an ajax autocomplete) and it takes more than your wait time for debounce then the requests could fire later making appear that debouncing is not working.
It's possible that your debounce function is taking longer to execute than the user to type. In this case, you want to make sure that you prevent double debouncing by passing in a third argument (immediate) as true.
The debounce function signature is: _.debounce(function, wait, [immediate])
So change the code to:
console.log("Pre Debounce");
var debounced = _.debounce(function() {
console.log("Field updated!");
}, 2000, true);
debounced();
Related
Every example of a debounce function that I've seen so far prevents an action from happening multiple times for a specified time span, and then executes the action one time when the specified time span has elapsed, then resets the timer. For example, the $mdUtil.debounce function that is included in Angular Material.
What I'm looking for is a debounce function that executes the action immediately and then prevents subsequent multiple actions from firing until the timer resets. This has the benefit of the user not having to wait until the debounce time has elapsed until their action is taken while still achieving the goal of debouncing the actions.
Has anyone seen one or had luck creating one?
Update After some more thought, the debounce function should fire the action immediately and then, if the debounced function was called again within the debounce time span, it should fire the action a second time before resetting the timer in case the second call changed any values.
edit: adding jsbin implementation
Lodash's debounce can do both. You'll have to specify whether it's leading or trailing.
https://lodash.com/docs#debounce
_.debounce(sendMail, 300, {
'leading': true,
'trailing': false
})
you can also write your own debounced function in just few lines jsbin example:
This will click first then debounce subsequent clicks.
function debounce(func, delay) {
console.log('debounce called with delay', delay);
var timer = 0;
return function debouncedFn() {
if (Date.now() - timer > delay) {
func();
}
timer = Date.now();
};
}
I have written a custom animation function. It usually works just fine, but when I call animate(); in rapid succession with different endCallbacks, sometimes the callbacks overlap really badly, causing the wrong action at the wrong time.
The problem is that the function instantiates multiple times and executes untill the endValue is reached. The currentValue is changed so fast that I get to see just the last value in my html page animation. This hiddes this unwanted behavior.
What I need when I call animate(); a second time is to end the first instance of animate(); and trigger a new one with new values and a new callback. Also at the same time I want to stop the setTimeout() function just to make sure no wrong callback is triggered.
window.onload = function(){
document.addEventListener('click', // some button
function (){
animate(1, 10);
}, false
);
}
function animate(startValue, endValue, callback, endCallback) {
var startValue = startValue,
currentValue = startValue,
endValue = endValue,
callback = callback,
timeout = null;
loopAnimation();
function loopAnimation(){
if (currentValue != endValue){
timeout = setTimeout(function(){
currentValue++;
// Callback executes some page manipulation code
if (typeof callback !== "undefined") callback(currentValue);
console.log(currentValue);
loopAnimation();
},500)
} else {
console.log("This callback triggers some specific changes in my page");
if (typeof endCallback !== "undefined") endCallback();
}
}
}
Instead of seeing in the console:
1,2,3, - 1,4,2,5 ... 6,9,7,10,8,9,10
I'd like to see just:
1,2,3, - 1,2 ... 7,8,9,10
However, keep in mind that because of the way I use animate() in my script I can't relly on knowing the name or scope of the input variables. This cuts me from being able to solve it myself.
While it isn't quite the implementation you're asking for, I wonder if Underscore's throttle or debounce would meet the need?
debounce will make sure your function is called no more than X times per second -- it'll still be executed once per every time called, but the subsequent calls will be delayed to meet your rate limit. So if you called animate twice in quick succession, debounce can delay the second execution until 100ms after the first or what have you.
throttle will basically ignore calls that occur during the rate limit. So if you call your animate 10 times within 100ms, you could have it throw out all but the first. (Actually, it'll do the first one, plus one at at the end of the wait period).
You don't need to use all of underscore to get these methods; I've seen people frequently copy and pasting just the debounce and/or throttle functions from underscore. If you google, you can find some standalone throttle or debounce implementations.
Throttle and debounce are commonly used in just your case, animation.
For your original spec, to actually "end the first instance of animate()" -- there's no great reliable way to do that in javascript. There's no real general purpose way to 'cancel' a function already being executed. If you can make it work with debounce or throttle, I think it will lead to less frustration.
What you need is to store the last timeout id you used. So next time you start a new animation, you clear any ongoing animation using this timeout id and clearTimeout.
I found convenient to store the interval on the function itself.
See the jsbin here :
http://jsbin.com/nadawezete/1/edit?js,console,output
window.onload = function(){
document.addEventListener('click', // some button
function (){
animate(1, 10);
}, false
);
};
function animate(startValue, endValue, callback, endCallback) {
var currentValue = startValue;
if (animate.timeout) clearTimeout(animate.timeout);
loopAnimation();
function loopAnimation(){
if (currentValue != endValue){
animate.timeout = setTimeout(function(){
console.log(currentValue);
currentValue++;
// Callback executes some page manipulation code
if (callback ) callback(currentValue);
loopAnimation();
},500);
} else {
console.log("This callback triggers some specific changes in my page");
if (endCallback) endCallback();
}
}
}
I have this problem:
I have real time form validation, but the inputs have autocomplete on, so when you choose a value from it, it wont trigger eny event and I cant start validating the new value.
Here is some solution which I have figured
I was thinking when input triggers onfocus event, i can start periodically (for example every one second) calling some validating functions on input, whether the value changes or not.
And on blur event I want to stop calling the function.
But I cant figure it out how to start call the function periodically...
What I have so far:
$('input[name="'+input_name+'"]').focus(function(){
checkInput(input_name);
});
function checkInput(input_name){
setInterval(doneTyping(input_name),700);
console.log(1);
}
But this only happens once, how could I do it?
Thanks.
setInterval needs an anonymous function
myInterval = setInterval(function(){doneTyping(input_name);},700);
//To clear
clearTimeout(myInterval);
do it like
var intervalID = window.setInterval(function(){ doneTyping(input_name); }, 700);
see this fiddle.
If I had a search box which loads suggestion on every keyup event. I think it would get worst for me server to respond on every request on every keyup event which could be about 10 times in a second or more. I mean there should be some AJAX request limiting functions that limit request(s) per sec(s) or any specific time. Can you tell me through coding examples on how to limit ajax request ?
Second thing, I would like to share that if we use JSP to limit AJAX request(s). Would it be a good idea being JSP a client side language and a malicious user could easily remove those ajax-request-limiting function(s) and doom the server.
OR ! Is there is any remedy for that ?
Thanks for reading.
You can write a debounce method by your self:
var timer = null;
searchInput.addEventListener('keyup', function (evt) {
clearTimeout(timer);
timer = setTimeout(function () {
// this event listener will postpone its execution until after 1 second have elapsed since the last time it was invoked
// send your ajax request here
}, 1000);
}, false);
Or you can use underscore to limit keyup event:
searchInput.addEventListener('keyup', _.debounce(function (evt) {
// this event listener will postpone its execution until after 1 second have elapsed since the last time it was invoked
}, 1000), false);
You need to set up a timer that will reset everytime an key event occurs. if no key was pressed until the timer is out , then send your ajax request.
I have a #search element, which when the keyup event occurs should fire a function. This function should only fire if keyup hasn't occurred in a set amount of time (say 500 milliseconds for example). This will prevent search results from updating every letter that is pressed. The problem is that with backbone.js, I have my events in a hash and the one that is applicable looks like:
'keyup #search' : 'setSearch'
which calls the setSearch() function when the keyup event occurs. I'm not really clear on how to handle it at this point. I've tried a variety of things, but nothing can maintain the timer past the function ending.
I have something like so:
setSearch: function(event) {
var timer = window.setTimeout( function() {
// run function here
alert('fired');
}, 500);
},
rather than the alert('fired'), I'll have my own function run. I can see why this code doesn't work (a timer is set for every keyup event that occurs. But I still don't have a clear idea on what else I could try.
What you are looking for is actually a function provided to you from underscore.js (a requirement of Backbone)
setSearch: _.throttle(function() {
//Do Stuff
}, 500),
In a nutshell, this returns a new form of the anonymous function that can only be called once every 500ms. You will likely have to tweak the timing to your needs.
More Info:
http://documentcloud.github.com/underscore/#throttle
You need an instance variable in your view that stores the timer ID, then you can stop it and restart it as needed:
setSearch: function(event) {
var self = this;
if(self.timer)
clearTimeout(self.timer);
self.timer = setTimeout(function() {
alert('fired');
self.timer = null;
}, 500);
}
So, if the timer is already running, you call clearTimeout to stop it, start a new timer, and store the timer ID in self.timer (AKA this.timer). You'll also want to reset the stored timer ID in the timer's callback function or your setSearch won't do anything after its timer has fired once. And all the self business is just to capture this for use in the timer's callback function.
Preventing the updating of search results on every keyup is exactly the kind of situation that Underscore's _.debounce(function, wait) function is meant to deal with. The underscore documentation for _.debounce() states:
Creates and returns a new debounced version of the passed function which will postpone its execution until after wait milliseconds have elapsed since the last time it was invoked. Useful for implementing behavior that should only happen after the input has stopped arriving.
Your refactored code would look as simple as:
setSearch: function(event) {
_.debounce(doSomething, 300);
},
Since you want your event handler events to be able to maintain whether or not an event has recentlyFired, you probably want to wrap your handler into a closure and maintain that status. The status should be changed to true when an event has fired, and reset to false after a delay of 500ms.
setSearch: function( ) {
var firedRecently = false;
return function(event) {
if (firedRecently) {
// it has fired recently. Do you want to do something here?
} else {
// not fired recently
firedRecently = true;
// run your function here
alert('fired');
var resetStatus = window.setTimeout( function () {
firedRecently = false;
}, 500);
}
}
}( );