how to fire only the very last setTimeout - javascript

window.addEventListener('resize', onResize, false);
function onResize() {
timer = setTimeout(function() {
console.log('fire timer');
}, 1000);
}
Resize event fires super fast. So there will be many timer fire every 1000ms. How to fire only the very last timer?

Clear the timer for each call
var timer;
function onResize() {
clearTimeout(timer);
//...
timer = setTimeout(function() {
console.log('fire timer');
}, 1000);
}
(timer may be null or undefined).

If you're already using underscore.js, you have the debounce method to fill this need:
Creates and returns a new debounced version of the passed function that 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.

Related

setTimeout inside setInterval does not work well

My code should change the class of the item in every second and repeat it forever.
function myFunction() {
setInterval(function() {
$("#item").addClass("class-one").removeClass("class-two");
setTimeout(function(){
$("#item").addClass("class-two").removeClass("class-one");
}, 1000);
},1000);
}
myFunction();
First time the code works well, but after the loop starts again, it starts switching very fast. Can anybody tell me why?
The interval starts
1 second later the interval resolves:
classes are switched over
the timeout is triggered
1 second later:
The timeout resolves
classes are switched over
The interval resolves
classes are switched over
the timeout is triggered
You probably want the timeout time to be half the interval time, not the same as it.
A better approach entirely would be to use one class and use jQuery().toggle to toggle it on and off every second (using one interval and no timeouts).
correct way :
var i = 0;
function myFunction() {
setInterval(function() {
if(i % 2 == 0) {
$("#item").addClass("class-one").removeClass("class-two");
} else {
$("#item").addClass("class-two").removeClass("class-one");
}
i++;
},1000);
}
myFunction();
or with your solution : ( increase 1 second of setInterval time )
function myFunction() {
setInterval(function() {
$("#item").addClass("class-one").removeClass("class-two");
setTimeout(function(){
$("#item").addClass("class-two").removeClass("class-one");
}, 1000);
},2000);
}
myFunction();

How to run a function 1 minute after the mousemove event?

I want to run a function after the last mousemove event. I've tried the code below:
#HostListener('document:mousemove', ['event'])
eventHandler(event) {
setTimeout(() => {
// do something
}, 60000);
}
The problem is, it fires in the first mousemove event and won't reset the time if another event occurs. How to reset it and start the setTimeout function each time the event occurs?
I'm not sure which framework you're using, but in general you need to store the Id of the timer and cancel it every time and start a new one - remembering to capture the Id.
var timerId = 0
document.addEventListener("mousemove",function(){
clearTimeout(timerId);
timerId = setTimeout(function(){
console.log("5 seconds since last mouse move");
},5000);
});
Well, when you call setTimeout, the return value is a number, representing the ID value of the timer that is set. Use this value with the clearTimeout() method to cancel the timer and set it again when a new event occurs. You can read more about setTimeout and clearTimeout here and here.
Basically you can have something like:
//global variable
var timeoutID;
[...]
#HostListener('document:mousemove', ['event'])
eventHandler(event) {
if (timeoutID)
clearTimeout(timeoutID);
timeoutID = setTimeout(() => {
// do something
}, 60000);
}
I provided a simple jsfiddle (pure js, no framework) too:
var timeoutValue = 2000;
var timeoutID;
document.addEventListener("mousemove", function() {
if (timeoutID)
clearTimeout(timeoutID);
timeoutID = setTimeout(function() {
console.log(timeoutValue + " just passed");
}, 2000);
});
Cheers!

Cancel timout / timer if function called again --- debounce function

I want to create a function that starts a timeout, but if the function is called again, before the timer ends, cancel the original call and start the timer again.
I thought I could do:
function setTimer() {
setTimeout(() => {
// do something
}, 3000)
}
...but that doesn't work, for every time I run setTimer(), it doesn't cancel the original call.
Can anyone point me in the right direction?
setTimeout returns an id you can use to clear that timeout with clearTimeout(). So you can clear the existing timeout at the beginning of your function.
For example if you keep clicking it will keep restarting -- if you don't click it finishes in 2 seconds:
let timerID;
function setTimer() {
console.log("starting/restarting timer")
clearTimeout(timerID)
timerID = setTimeout(() => {
console.log("finished")
}, 2000)
}
<p onclick="setTimer()">click to start</p>
What you want to do is cancel the existing timeout and start it over? You can do this by using cleartimeout
let timeoutFunctionVar = null;
const setTimeoutFunction = () => {
clearTimeout(timeoutFunctionVar)
timeoutFunctionVar = setTimeout(() => {
// do something
}, 3000)
};
setTimeoutFunction()
So every time setTimeoutFunction() gets called, the previous timeout gets reset
I figured this question gets asked frequently, especially for searches triggered by key events, but I couldn't find any.
The basic idea is that you keep the timeout id stateful, so you can clear it on subsequent invocations to the TO setter:
const MS_IN_SEC = 1000;
let old_timeout;
function TO_setter(searchString) {
if (old_timeout)
window.clearTimeout(old_timeout);
old_timeout = window.setTimeout(search, 2 * MS_IN_SEC, searchString);
}
function search(s) {
console.log('search for: %s', s);
}

How to stop a self-called setTimeout() function?

I have a count down function. The function used setTimeout() to repeatedly call itself:
function countDownSendCode(timer) {
if(timer >= 0) {
document.querySelector('#send-code').setAttribute('disabled', true);
document.querySelector('#send-code').innerHTML = timer + 's later resend';
setTimeout(function() {
countDownSendCode(timer - 1);
}, 1000);
} else {
document.querySelector('#send-code').removeAttribute('disabled');
document.querySelector('#send-code').innerHTML = 'Send';
}
}
The document.querySelector('#send-code') is a button used to send code. When a user click the button, he cannot click it again until the count down over.
I added below function to the button's click event to call the count down:
function clickSendCode(ev) {
ev.preventDefault();
countDownSendCode(5); // call the count down here
handleAjaxRequest();
}
In some case, in the handleAjaxRequest(), I need to stop the count down and make the button available immediately.
I can call countDownSendCode(-1) to set the button available, but how can I clear the setTimeout()? Because it called by it self, I cannot get the timeID required by clearTimeout().
You can achieve this functionality as shown in the following code snippet:
// global var serving as a handle to Timer
var _timer;
// call this function to start timer
function StartMyTimer()
{
_timer = setTimeout(function(){ alert("Hello, Timer is Running!"); }, 5000);
}
// call this function to stop timer
function StopMyTimer()
{
clearTimeout(_timer);
}
I would also suggest you to consider a pair of functions: setInterval() and clearInterval() which may simplify the coding of repetitive tasks.
Hope this will help.
I'd suggest not recursively calling countDownSendCode(). Rather just set the timer to the correct number of seconds to begin with, then you can return a ref to the timer and pass it to the ajax handler.
function countDownSendCode(timer) {
if(timer >= 0) {
document.querySelector('#send-code').setAttribute('disabled', true);
document.querySelector('#send-code').innerHTML = timer + 's later resend';
countDownSendCode._timer = setTimeout(function() {
countDownSendCode(timer - 1);
}, 1000);
}
else {
if('stop'===timer){
clearTimeout(countDownSendCode._timer);
}
document.querySelector('#send-code').removeAttribute('disabled');
document.querySelector('#send-code').innerHTML = 'Send';
}
}
modify the countDownSendCode function as above. call it with 'stop' string when you need the button to be available immediately.

Can I stop the execution of a function from outside that function?

I have this code:
function toStop(){
while(true){}
}
toStop();
Now, how can I stop this? Or how can I kill the current thread if this function call is somewhere in the setInterval running thread? Example:
var id = setInterval(function(){
toStop();
}, 1000);
//stop thread/timer with id here.
clearInterval doesn't work because it waits until the function call ends.
Thanks!
"Can I stop the execution of a function from outside that function?"
No, you can't programmatically.
JavaScript is single-threaded and if you run a piece of code that makes it infinitely busy, such as while(true);, then nothing else will ever be able to execute.
Calling such a piece of code within setTimeout or setInterval will have the same result, since the callback of these gets executed in the only thread we have as well.
However, you can create a timed recurring execution using setInterval or setTimeout, which can be stopped.
var timerId = setInterval(function () {
//Process an iteration of the loop in here
//If you cause an infinite loop in here, you will have the same issue
}, 50);
//stop the timer after ~3 seconds
setTimeout(clearInterval.bind(null, timerId), 3000);
Notes:
4 is the lowest interval that could be honored as specified in the SPEC.
setInterval will stack if the callback takes more time to execute than the specified interval. For that reason I never use setInterval and always use setTimeout.
Timer intervals are not guaranteed to be accurate
e.g. with setTimeout
var stopProcessing = startProcessing();
//Stop processing after ~3 seconds
setTimeout(stopProcessing, 3000);
function startProcessing() {
var timerId;
!function process() {
//Do some processing
//Continue processing in ~50 ms
timerId = setTimeout(process, 50);
}();
return function () { clearTimeout(timerId); }
}
Instead of an infinite loop, just use an if statement and wrap it in an interval:
var shouldContinue = true;
var interval = 0;
function toStop() {
if (interval == 0) {
interval = setInterval(function() {
if(shouldContinue) {
...
}
else {
clearInterval(interval);
interval = 0;
}
}, 200); // Or whatever interval makes sense
}
}
toStop();
// ...
shouldContinue = false;
See this principle in action here.
No, you can't programmatically, as #plalx said but you could try this: declaring a binding outside and check on that to continue or stop the loop:
let letMeGoOut;
function toStop(){
while(letMeGoOut != false)
}
toStop();
Here, I've created a function on mouseover that triggers a loop changing the opacity of the h1. It goes on till the mouse cursor moves out and is over something else in the page.
Here is the example: https://codepen.io/Mau-Di-Bert/pen/VqrRxE

Categories