How to abort function by timeout at JavaScript - 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);

Related

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

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

Stopping Nested Timeouts in Javascript

I want to execute a piece of arbitrary code and be able to stop it whenever I want. I figured I could do this with setTimeout and then use clearTimeout to stop it. However if the code in the timeout creates it's own timeouts, then those keep executing even after I clear the original.
Example:
var timeoutID = setTimeout(
function(){
console.log("first event can be stopped with clearTimout(timeoutID)");
setTimeout(function(){console.log("but not this one")}, 5000)
}, 5000)
Now one way would be to control the code being executed and make it store the value of any additional timeouts into a global variable and clear them all at once. But is there a better way to do this? And is there a way to do this on arbitrary code?
To clarify, I'm trying to be able to execute any function I want, then stop it whenever I want, even if the function contains timeouts
You can put the inner timeout into a variable too:
var innerTimeout,
timeoutID = setTimeout(
function(){
console.log("first event can be stopped with clearTimout(timeoutID)");
innerTimeout = setTimeout(function(){console.log("but not this one")}, 5000);
}, 5000);
You would have to create an array of timeout IDs such as this:
var timeoutIds = [];
timeoutIds.push(setTimeout(
function(){
console.log("first event can be stopped with clearTimout(timeoutID)");
timeoutIds.push(setTimeout(function(){console.log("but not this one")}, 5000));
}, 5000))
And then to clear:
for (int i = 0; i < timeoutIds.length; i++)
{
clearTimeout(timeoutIds[i]);
}
timeoutIds = [];
You could wrap your timeouts in an object or re use timeoutID for the second timeout.
Wrap in an object:
function Timer(){
var me=this;
this.currentTimerID=setTimeout(function(){
console.log("First timeout");
me.currentTimerID=setTimeout(function(){
console.log("Second timeout");
},100);
},100);
};
Timer.prototype.cancel=function(){
clearTimeout(this.currentTimerID);
};
var t = new Timer();//let this run it's course
setTimeout(function(){t = new Timer()},250);//start timer again
setTimeout(function(){t.cancel();},400);// cancel it after the first timeout
Re use timeoutID:
var timeoutID = setTimeout(
function(){
console.log("first event can be stopped with clearTimout(timeoutID)");
timeoutID=setTimeout(function(){console.log("but not this one")}, 100)
}, 100)
setTimeout(function(){
clearTimeout(timeoutID);
},150);// will not execute the second timeout
One tip: If you're testing code with timeout then don't use such high values as it'll take 10 seconds for your original code to run.

Wait 5 seconds before executing next line

This function below doesn’t work like I want it to; being a JS novice I can’t figure out why.
I need it to wait 5 seconds before checking whether the newState is -1.
Currently, it doesn’t wait, it just checks straight away.
function stateChange(newState) {
setTimeout('', 5000);
if(newState == -1) {
alert('VIDEO HAS STOPPED');
}
}
Browser
Here's a solution using the new async/await syntax.
Be sure to check browser support as this is a language feature introduced with ECMAScript 6.
Utility function:
const delay = ms => new Promise(res => setTimeout(res, ms));
Usage:
const yourFunction = async () => {
await delay(5000);
console.log("Waited 5s");
await delay(5000);
console.log("Waited an additional 5s");
};
The advantage of this approach is that it makes your code look and behave like synchronous code.
Node.js
Node.js 16 provides a built-in version of setTimeout that is promise-based so we don't have to create our own utility function:
import { setTimeout } from "timers/promises";
const yourFunction = async () => {
await setTimeout(5000);
console.log("Waited 5s");
await setTimeout(5000);
console.log("Waited an additional 5s");
};
⚠️ Just for the record, you might be tempted to use a wait function to circumvent race conditions (when testing asynchronous code for example). This is rarely a good idea.
You have to put your code in the callback function you supply to setTimeout:
function stateChange(newState) {
setTimeout(function () {
if (newState == -1) {
alert('VIDEO HAS STOPPED');
}
}, 5000);
}
Any other code will execute immediately.
You really shouldn't be doing this, the correct use of timeout is the right tool for the OP's problem and any other occasion where you just want to run something after a period of time. Joseph Silber has demonstrated that well in his answer. However, if in some non-production case you really want to hang the main thread for a period of time, this will do it.
function wait(ms){
var start = new Date().getTime();
var end = start;
while(end < start + ms) {
end = new Date().getTime();
}
}
With execution in the form:
console.log('before');
wait(7000); //7 seconds in milliseconds
console.log('after');
I've arrived here because I was building a simple test case for sequencing a mix of asynchronous operations around long-running blocking operations (i.e. expensive DOM manipulation) and this is my simulated blocking operation. It suits that job fine, so I thought I post it for anyone else who arrives here with a similar use case. Even so, it's creating a Date() object in a while loop, which might very overwhelm the GC if it runs long enough. But I can't emphasize enough, this is only suitable for testing, for building any actual functionality you should refer to Joseph Silber's answer.
If you're in an async function you can simply do it in one line:
console.log(1);
await new Promise(resolve => setTimeout(resolve, 3000)); // 3 sec
console.log(2);
FYI, if target is NodeJS you can use this built-in function if you want (it's a predefined promisified setTimeout function):
import { setTimeout } from 'timers/promises';
await setTimeout(3000); // 3 sec
Use a delay function like this:
var delay = ( function() {
var timer = 0;
return function(callback, ms) {
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
Usage:
delay(function(){
// do stuff
}, 5000 ); // end delay
Credits: How to delay the .keyup() handler until the user stops typing?
You should not just try to pause 5 seconds in javascript. It doesn't work that way. You can schedule a function of code to run 5 seconds from now, but you have to put the code that you want to run later into a function and the rest of your code after that function will continue to run immediately.
For example:
function stateChange(newState) {
setTimeout(function(){
if(newState == -1){alert('VIDEO HAS STOPPED');}
}, 5000);
}
But, if you have code like this:
stateChange(-1);
console.log("Hello");
The console.log() statement will run immediately. It will not wait until after the timeout fires in the stateChange() function. You cannot just pause javascript execution for a predetermined amount of time.
Instead, any code that you want to run delays must be inside the setTimeout() callback function (or called from that function).
If you did try to "pause" by looping, then you'd essentially "hang" the Javascript interpreter for a period of time. Because Javascript runs your code in only a single thread, when you're looping nothing else can run (no other event handlers can get called). So, looping waiting for some variable to change will never work because no other code can run to change that variable.
setTimeout(function() {
$('.message').hide();
}, 5000);
This will hide the '.message' div after 5 seconds.
This solution comes from React Native's documentation for a refresh control:
function wait(timeout) {
return new Promise(resolve => {
setTimeout(resolve, timeout);
});
}
To apply this to the OP's question, you could use this function in coordination with await:
await wait(5000);
if (newState == -1) {
alert('Done');
}
Try this:
//the code will execute in 1 3 5 7 9 seconds later
function exec() {
for(var i=0;i<5;i++) {
setTimeout(function() {
console.log(new Date()); //It's you code
},(i+i+1)*1000);
}
}
Best way to create a function like this for wait in milli seconds, this function will wait for milliseconds provided in the argument:
function waitSeconds(iMilliSeconds) {
var counter= 0
, start = new Date().getTime()
, end = 0;
while (counter < iMilliSeconds) {
end = new Date().getTime();
counter = end - start;
}
}
Based on Joseph Silber's answer, I would do it like that, a bit more generic.
You would have your function (let's create one based on the question):
function videoStopped(newState){
if (newState == -1) {
alert('VIDEO HAS STOPPED');
}
}
And you could have a wait function:
function wait(milliseconds, foo, arg){
setTimeout(function () {
foo(arg); // will be executed after the specified time
}, milliseconds);
}
At the end you would have:
wait(5000, videoStopped, newState);
That's a solution, I would rather not use arguments in the wait function (to have only foo(); instead of foo(arg);) but that's for the example.
You can add delay by making small changes to your function ( async and await ).
const addNSecondsDelay = (n) => {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, n * 1000);
});
}
const asyncFunctionCall = async () {
console.log("stpe-1");
await addNSecondsDelay(5);
console.log("step-2 after 5 seconds delay");
}
asyncFunctionCall();
If you have an asyn function you can do:
await new Promise(resolve => setTimeout(resolve, 5000));

Execute the setInterval function without delay the first time

It's there a way to configure the setInterval method of javascript to execute the method immediately and then executes with the timer
It's simplest to just call the function yourself directly the first time:
foo();
setInterval(foo, delay);
However there are good reasons to avoid setInterval - in particular in some circumstances a whole load of setInterval events can arrive immediately after each other without any delay. Another reason is that if you want to stop the loop you have to explicitly call clearInterval which means you have to remember the handle returned from the original setInterval call.
So an alternative method is to have foo trigger itself for subsequent calls using setTimeout instead:
function foo() {
// do stuff
// ...
// and schedule a repeat
setTimeout(foo, delay);
}
// start the cycle
foo();
This guarantees that there is at least an interval of delay between calls. It also makes it easier to cancel the loop if required - you just don't call setTimeout when your loop termination condition is reached.
Better yet, you can wrap that all up in an immediately invoked function expression which creates the function, which then calls itself again as above, and automatically starts the loop:
(function foo() {
...
setTimeout(foo, delay);
})();
which defines the function and starts the cycle all in one go.
I'm not sure if I'm understanding you correctly, but you could easily do something like this:
setInterval(function hello() {
console.log('world');
return hello;
}(), 5000);
There's obviously any number of ways of doing this, but that's the most concise way I can think of.
I stumbled upon this question due to the same problem but none of the answers helps if you need to behave exactly like setInterval() but with the only difference that the function is called immediately at the beginning.
Here is my solution to this problem:
function setIntervalImmediately(func, interval) {
func();
return setInterval(func, interval);
}
The advantage of this solution:
existing code using setInterval can easily be adapted by substitution
works in strict mode
it works with existing named functions and closures
you can still use the return value and pass it to clearInterval() later
Example:
// create 1 second interval with immediate execution
var myInterval = setIntervalImmediately( _ => {
console.log('hello');
}, 1000);
// clear interval after 4.5 seconds
setTimeout( _ => {
clearInterval(myInterval);
}, 4500);
To be cheeky, if you really need to use setInterval then you could also replace the original setInterval. Hence, no change of code required when adding this before your existing code:
var setIntervalOrig = setInterval;
setInterval = function(func, interval) {
func();
return setIntervalOrig(func, interval);
}
Still, all advantages as listed above apply here but no substitution is necessary.
You could wrap setInterval() in a function that provides that behavior:
function instantGratification( fn, delay ) {
fn();
setInterval( fn, delay );
}
...then use it like this:
instantGratification( function() {
console.log( 'invoked' );
}, 3000);
Here's a wrapper to pretty-fy it if you need it:
(function() {
var originalSetInterval = window.setInterval;
window.setInterval = function(fn, delay, runImmediately) {
if(runImmediately) fn();
return originalSetInterval(fn, delay);
};
})();
Set the third argument of setInterval to true and it'll run for the first time immediately after calling setInterval:
setInterval(function() { console.log("hello world"); }, 5000, true);
Or omit the third argument and it will retain its original behaviour:
setInterval(function() { console.log("hello world"); }, 5000);
Some browsers support additional arguments for setInterval which this wrapper doesn't take into account; I think these are rarely used, but keep that in mind if you do need them.
Here's a simple version for novices without all the messing around. It just declares the function, calls it, then starts the interval. That's it.
//Declare your function here
function My_Function(){
console.log("foo");
}
//Call the function first
My_Function();
//Set the interval
var interval = window.setInterval( My_Function, 500 );
There's a convenient npm package called firstInterval (full disclosure, it's mine).
Many of the examples here don't include parameter handling, and changing default behaviors of setInterval in any large project is evil. From the docs:
This pattern
setInterval(callback, 1000, p1, p2);
callback(p1, p2);
is identical to
firstInterval(callback, 1000, p1, p2);
If you're old school in the browser and don't want the dependency, it's an easy cut-and-paste from the code.
I will suggest calling the functions in the following sequence
var _timer = setInterval(foo, delay, params);
foo(params)
You can also pass the _timer to the foo, if you want to clearInterval(_timer) on a certain condition
var _timer = setInterval(function() { foo(_timer, params) }, delay);
foo(_timer, params);
For someone needs to bring the outer this inside as if it's an arrow function.
(function f() {
this.emit("...");
setTimeout(f.bind(this), 1000);
}).bind(this)();
If the above producing garbage bothers you, you can make a closure instead.
(that => {
(function f() {
that.emit("...");
setTimeout(f, 1000);
})();
})(this);
Or maybe consider using the #autobind decorator depending on your code.
You can set a very small initial delay-time (e.g. 100) and set it to your desired delay-time within the function:
var delay = 100;
function foo() {
console.log("Change initial delay-time to what you want.");
delay = 12000;
setTimeout(foo, delay);
}
To solve this problem , I run the function a first time after the page has loaded.
function foo(){ ... }
window.onload = function() {
foo();
};
window.setInterval(function()
{
foo();
}, 5000);
This example builds on #Alnitak's answer, but uses await Promise for finer granularity of control within the loop cycle.
Compare examples:
let stillGoing = true;
(function foo() {
console.log('The quick brown fox did its thing');
if (stillGoing) setTimeout(foo, 5000);
})();
foo();
In the above example we call foo() and then it calls itself every 5 seconds.
But if, at some point in the future, we set stillGoing to false in order to stop the loop, we'll still get an extra log line even after we've issued the stop order. This is because at any given time, before we set stillGoing to false the current iteration will have already created a timeout to call the next iteration.
If we instead use await Promise as the delay mechanism then we have an opportunity to stop the loop before calling the next iteration:
let stillGoing = true;
(async function foo() {
console.log('The quick brown fox did its thing');
await new Promise(resolve => setTimeout(resolve, 5000));
if (stillGoing) foo();
})();
foo();
In the second example we start by setting a 5000ms delay, after which we check the stillGoing value and decide whether calling another recursion is appropriate.
So if we set stillGoing to false at any point, there won't be that one extra log line printed after we set the value.
The caveat is this requires the function to be async, which may or may not be an option for a given use.
For Those using React, here is how I solve this problem:
const intervalRef = useRef(0);
useEffect(() => {
if (condition is true){
if (intervalRef.current === 0) {
callMyFunction();
}
const interval = setInterval(() => {
callMyFunction();
}, 5_000);
intervalRef.current = interval;
} else {
clearInterval(intervalRef.current);
}
}, [deps]);
// YCombinator
function anonymous(fnc) {
return function() {
fnc.apply(fnc, arguments);
return fnc;
}
}
// Invoking the first time:
setInterval(anonymous(function() {
console.log("bar");
})(), 4000);
// Not invoking the first time:
setInterval(anonymous(function() {
console.log("foo");
}), 4000);
// Or simple:
setInterval(function() {
console.log("baz");
}, 4000);
Ok this is so complex, so, let me put it more simple:
function hello(status ) {
console.log('world', ++status.count);
return status;
}
setInterval(hello, 5 * 1000, hello({ count: 0 }));
If you can use RxJS, there is something called timer():
import { Subscription, timer } from 'rxjs';
const INITIAL_DELAY = 1;
const INTERVAL_DELAY = 10000;
const timerSubscription = timer(INITIAL_DELAY, INTERVAL_DELAY)
.subscribe(() => {
this.updateSomething();
});
// when destroying
timerSubscription.unsubscribe();
With ES2017, it may be preferable to avoid setInterval altogether.
The following solution has a much cleaner execution flow, prevents issues if the function takes longer than the desired time to complete, and allows for asynchronous operations.
const timeout = (delayMs) => new Promise((res, _rej) => setTimeout(res, delayMs));
const DELAY = 1_000;
(async () => {
while (true) {
let start_time = Date.now();
// insert code here...
let end_time = Date.now();
await timeout(DELAY - (end_time - start_time));
}
})();
There's a problem with immediate asynchronous call of your function, because standard setTimeout/setInterval has a minimal timeout about several milliseconds even if you directly set it to 0. It caused by a browser specific work.
An example of code with a REAL zero delay wich works in Chrome, Safari, Opera
function setZeroTimeout(callback) {
var channel = new MessageChannel();
channel.port1.onmessage = callback;
channel.port2.postMessage('');
}
You can find more information here
And after the first manual call you can create an interval with your function.
actually the quickest is to do
interval = setInterval(myFunction(),45000)
this will call myfunction, and then will do it agaian every 45 seconds which is different than doing
interval = setInterval(myfunction, 45000)
which won't call it, but schedule it only

Categories