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

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);
}

Related

Is there any way to make javascript setTimeout pause when I switch to another tab?

I am having some task that is executing, and when it finishes I show an animation and redirect the user to another page. I am using setTimeout method when redirecting to another page to wait for the whole animation to finish. However, if the user goes to another tab and the event that triggers the animation and redirection fires the user goes back to tab, but doesn't see the animation. Is there any way to tell the setTimeout method to wait until that tab is in active again?
No, but you can capture the timer id you get from setTimeout as well as the clock time you started that timer (e.g. using Date.now()), then call clearTimeout for that id when the document blur event triggers, recording what the remaining time should be based on the current time and the time you saved when you created the timeout, and when the document focus event triggers, start a new timeout with that remaining time as timeout value.
E.g. something like this (but obviously, you want to turn this into something better, with some administrative functions instead of inlining everything)
const timerData = {
interval: DEFAULT_INTERVAL,
start: Date.now(),
id: setTimout(..., DEFAULT_INTERVAL)
};
document.addEventListener(`blur`, () => {
clearTimeout(timerData.id);
timerData.remaining = timerData.start + timerData.interval - Date.now();
});
document.addEventListener(`focus`, () => {
if (timerData.remaining) {
timerData.interval = timerData.remaining
timerData.remaining = undefined;
timerData.start = Date.now();
timerData.id = setTimeout(..., timerData.interval);
}
});
You can create a Timeout class with a start/stop API. Then you can instantiate the class somewhere in your code, and after start or stop it based on user interface events.
Class
class Timeout {
constructor(duration, callback) {
this.duration = duration
this.callback = callback
}
get timeout() { return this._timeout }
set timeout(timeout) { this._timeout = timeout }
get duration() { return this._duration }
set duration(duration) { this._duration = duration }
get callback() { return this._callback }
set callback(callback) { this._callback = callback }
start() {
this.timeout = setTimeout(this.callback, this.duration)
return this
}
stop() {
clearTimeout(this.timeout)
return this
}
}
Instantiation
let timeout = new Timeout(
1000,
() => {
console.log('timeout')
}
)
Start Timeout
timeout.start()
Stop Timeout
timeout.stop()

Debounce function returning multiples times

Context: It's the first time I try to implement a debounce() function and probably I misunderstood something, because the function it's calling my API multiple times, although the delay it's been aplied, my code:
async updateSelectAll(value) {
const execute = this.debounce(async () => {
await this.getTotalDaeMunicipio(this.filtroDaeMunicipio);
await this.gerarGraficoMunicipio();
}, 1000);
execute();
},
debounce(func, wait) {
let timer = null;
return () => {
clearTimeout(timer);
timer = setTimeout(func, wait);
};
}
The function updateSelectAll it's called everytime the user clicks on a checkbox, this is working.
The problem: when the user clicks on the checkbox, the function updateSelectAll it's called, after 1 second(1000ms), the API is called through the function execute() which have the debounce function, but when the user click multiple times on the checkbox, the API is called multiple times.
Expected behavior: when the checkobox it's clicked multiple times, was meant to do just one call to the API.
You create a local variable timer in the debounce function that the inner function closes over and has access to.
debounce(func, wait) {
let timer = null;
return () => {
clearTimeout(timer);
timer = setTimeout(func, wait);
};
}
The problem is, though, that you invoke this.debounce() multiple times, which is not sharing this timer. You need to share this timer between invocations of debounce to achieve your goal

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!

Countdown timer increase on user interaction

Consider this C# question: Countdown timer increase on interaction?
I am in need of an equivalent for Javascript. That is, the following:
Problem
I need a callback to be called after some time T however if user interaction happens before the callback is executed then this time T must be increased by some number X. How is this modelled using Javascript?
Background (the why)
I have a page forward button which upon being clicked increases the page by 1. Increasing the page causes some hefty computations to happen so it'd be preferable to only switch page after some small time frame so that a user that's spamming the page button won't make the programs perf. go in the bin.
If I understand correctly, you can simply clear the timeout and set it again.
clearTimeout(timer);
timer = setTimeout(function(){}, 1000);
I made a quick codepen with an example. I hope it helps: https://codepen.io/daniti/pen/gjePdo
You can use a debounce function. It sets a timer and if an interation happens before timer expiration it deletes the old timer and creates a new one, effectively resets the time. Example:
function debounce(fn, delay) {
let timerId;
return function (...args) {
if (timerId)
clearTimeout(timerId);
timerId = setTimeout(() => {
fn(...args);
timerId = null;
}, delay);
}
}
You can use a timeout : https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout
document.getElementById("spamMe").onclick = doLongAction;
var canDoLongAction = true;
var timeout;
function doLongAction() {
if (canDoLongAction) {
// prevent relaunch of action
canDoLongAction = false
// the action will become available in 2sec
timeout = window.setTimeout(
() => {canDoLongAction = true},
2000
)
// you do your long action
alert("hey")
} else {
// if clicked while the timeout is going reset it
window.clearTimeout(timeout);
timeout = window.setTimeout(
() => {canDoLongAction = true},
2000
)
}
}
<button id="spamMe">spam me!!</button>
in the example, the button is blocked until you stop clicking for 2 seconds

How to abort function by timeout at JavaScript

I need to execute some function but if timeout expired I need to abort this function. I tried to use setTimeout and setImmediate. I wrote this example and used setInterval in callback for checking of working but it did't help me:
function waiter(timeout, fun) {
var functionHandler = setImmediate(fun);
var timeoutHandler = setTimeout(() => {
console.log('stoped');
clearImmediate(functionHandler);
clearTimeout(timeoutHandler);
}, timeout);
fun();
}
waiter(5000, () => {
setInterval(() => {
console.log('work');
}, 500);
});
After clearImmediate and clearTimeout my interval still working. How I understood that method doesn't guarantee that my function will be aborted.
Does anybody have an idea how to abort function execution?
UPDATE:
Yes, I know that I should call clearInterval but it's just an example for work checking. For example I should parse some big data and if it doesn't can do it by timeout I need to cancel this function execution and call something else.
The clearXXX functions don't abort functions that are currently running, they just remove functions from the async queue, so that they never start to run. JavaScript always executes code to its completion. You cannot interrupt code that's running in the main thread.
You need to name your interval
and clear it by its name. So your interval is no longer anonymous, but you've got a reference for it.
let counter = 10;
let newYearCountdown = setInterval( () => {
console.log(counter);
counter--
if (counter === 0) {
console.log("HAPPY NEW YEAR!!");
// here i clear interval by its name.
clearInterval(newYearCountdown);
}
}, 1000);

Categories