Force setInterval to go to next step in loop - javascript

Is it possible to force setInterval to go to next step like this?:
global.timers = [] ;
var index = global.timers.length ;
global.timers[index] = setInterval(()=>{
// ...
},15000);
// pseudo code
if (event1 occured)
global.timers[x].step++ // Don't wait for remained time and go to next tick

You could simply clear the interval and create it again
global.timers = [] ;
var index = global.timers.length ;
global.timers[index] = setInterval(fn,15000);
// pseudo code
if (event1 occured) {
clearInterval(global.timers[x]);
global.timers[index] = setInterval(fn,15000);
}
function fn() {
}

You could store the id in the timers array and extract the event handler function then you would have more control over when to call the event handler.
Create a handler function for the event
When pushing to the global timers array, remember to store a name and the timer id.
If the event occured before the timer was called through setInterval, stop the timer and call the handler directly.
Example:
function coolEventHandler () {
console.log('handling cool event');
}
timers[0] = { name: 'coolEvent', id: setInterval(coolEventHandler, 15000) };
if('coolEvent') {
// find the correct timer
timer = timers.find(t => t.name === 'coolEvent');
// stop the timer so the callback is never invoked
// you probably want to remove it from the global timers array here too.
clearInterval(timer.id);
// call the handler directly
coolEventHandler();
}

Related

Bind setInterval() to a jQuery element

I have a toast notification system which displays a notification for 10 seconds before fading out. I want to add a functionality that pauses the countdown for fading out when the notification is hovered, and resumes when no longer hovering.
I'm trying to use the setInterval() function to do this, but I'm unsure how to later pause (clear) this interval. I know I can bind the setInterval to a variable, but these notifications are created dynamically, so I cannot bind them to a single variable.
In the example below, I store the setInterval() in a variable named ???, I would ideally bind this interval using the jQuery element ($(this)) as a variable so it's always unique, and can easily be cleared by passing the same jQuery element through the clearInterval() function.
Is there any way of doing this, or am I going about building this system all wrong?
// Start notification countdown
$.countDownNotification = function(notification) {
// IMPORTANT: Store the setInterval in a element-specific variable?
var ??? = setInterval( function() {
// Counts down from remaining seconds
var remaining = notification.attr('data-timer') + 1 - 1;
// Stores remaining seconds in data-attribute
notification.attr('data-timer', remaining);
// Remove notification when timer is on 0
if ( remaining == 0 ) {
notification.remove();
}
}, 1000);
}
// Pause on hover
$('.notification').on('mouseenter', function(e) {
// IMPORTANT: Clear the elemnt-specific interval
clearInterval(???);
});
// Resume when hover ends
$('.notification').on('mouseleave', function(e) {
var notification = $(this)
$.countDownNotification(notification);
});
You can store the interval on the notification via .data().
notification.data('int', setInterval(...
Then, in the event callbacks you can reference the interval via
$(this).data('int')
Also, note + 1 - 1 doesn't do anything meaningful.
Consider declaring a global variable.
// Start notification countdown
$.countDownNotification = function(notification) {
// IMPORTANT: Store the setInterval in a element-specific variable?
timer = setInterval( function() {
// Counts down from 10 and stores new value in data-attribute
notification.attr('data-timer', notification.attr('data-timer') - 1);
}, 1000);
// Remove notification when timer is on 0
if ( newRemaining == 0 ) {
notification.remove();
}
}
// `false` means no timer has been set
var timer = false;
// Pause on hover
$('.notification').on('mouseenter', function(e) {
// IMPORTANT: Clear the elemnt-specific interval
clearInterval( timer );
});
// Resume when hover ends
$('.notification').on('mouseleave', function(e) {
var notification = $(this)
$.countDownNotification(notification);
});
Another way to not set a global object is to return setInterval() by .countDownNotification.
// Start notification countdown
$.countDownNotification = function(notification) {
// IMPORTANT: Store the setInterval in a element-specific variable?
var id = setInterval( function() {
// Counts down from 10 and stores new value in data-attribute
notification.attr('data-timer', notification.attr('data-timer') - 1);
}, 1000);
// Remove notification when timer is on 0
if ( newRemaining == 0 ) {
notification.remove();
}
return id;
}
( function() {
// `false` means no timer has been set
var timer = false;
// Pause on hover
$('.notification').on('mouseenter', function(e) {
// IMPORTANT: Clear the elemnt-specific interval
clearInterval( timer );
});
// Resume when hover ends
$('.notification').on('mouseleave', function(e) {
var notification = $(this)
timer = $.countDownNotification(notification);
});
})();

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

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.

Stopping .each execution on every iteration for a spesific time interval in jquery?

I am trying to create the following functionality in my javascript:
$("mySelector").each(function(){
// Do something (e.g. change div class attribute)
// call to MyFunction(), the iteration will stop here as long as it will take for myFunction to complete
});
function myFunction()
{
// Do something for e.g. 5 seconds
}
My question is how can I stop every iteration for the duration of the myFunction()?
No, that isnt possible. You'll have to code it differently, possibly with a setTimeout based on the current index of .each.
$("mySelector").each(function(i){
// Do something (e.g. change div class attribute)
// call to MyFunction(), the iteration will stop here as long as it will take for myFunction to complete
setTimeout(myFunction,i*5000);
});
function myFunction()
{
// Do something for e.g. 5 seconds
}
Edit: You can also do it with queuing: http://jsfiddle.net/9Bm9p/6/
$(document).ready(function () {
var divs = $(".test");
var queue = $("<div />");
divs.each(function(){
var _this = this;
queue.queue(function(next) {
myFunction.call(_this,next);
});
});
});
function myFunction(next) {
// do stuff
$(this).doSomething();
// simulate asynchronous event
var self = this;
setTimeout(function(){
console.log(self.id);
// go to next item in the queue
next();
},2000);
}
​
Here's a jsFiddle that I think will do what you need:
http://jsfiddle.net/9Bm9p/2/
You would just need to replace the selector with what you use.
The "loop" that is occurring will wait for myFunction to finish before moving on to the next element. I added the setTimeout inside of myFunction to simulate it taking a period of time. If you are using asynchronous things, such as an AJAX request, you would need to put the call to myFunction inside of the complete method...or in the callback of an animation.
But as someone already commented, if everything in myFunction is synchronous, you should be able to use it as you are. If you are looking for this process to be asynchronous, or if things in myFunction are asynchronous, you cannot use a for loop or .each().
(function () {
"use strict";
var step = 0;
var content = $("mySelector");
var max = content.length;
var speed = 5000; // ms
var handle = setInterval(function () {
step++;
if (step >= max) {
clearInterval(handle);
} else {
var item = content[step];
// do something
}
}, speed);
}());
setInterval will do it once-every-n-miliseconds, and clearInterval will stop it when you're done. This won't lock up the browser (provided your "do something" also doesn't). FRAGILE: it assumes that the results of $("mySelector") are valid for the duration of the task. If that isn't the case then inside do something then validate item again.

Executing code after clearInterval

I have a setInterval calling a loop which displays an animation.
When I clearInterval in response to a user input, there are possibly one or more loop callbacks in queue. If I put a function call directly after the clearInterval statement, the function call finishes first (printing something to screen), then a queued loop callback executes, erasing what I wanted to print.
See the code below.
function loop() {
// print something to screen
}
var timer = setInterval(loop(), 30);
canvas.onkeypress = function (event) {
clearInterval(timer);
// print something else to screen
}
What's the best way to handle this? Put a delay on the // print something else to screen? Doing the new printing within the loop?
Edit: Thanks for the answers. For future reference, my problem was that the event that triggered the extra printing was buried within the loop, so once this executed, control was handed back to the unfinished loop, which then overwrote it. Cheers.
You could also use a flag so as to ignore any queued functions:
var should;
function loop() {
if(!should) return; // ignore this loop iteration if said so
// print something to screen
}
should = true;
var timer = setInterval(loop, 30); // I guess you meant 'loop' without '()'
canvas.onkeypress = function (event) {
should = false; // announce that loop really should stop
clearInterval(timer);
// print something else to screen
}
First of all, you probably meant:
var timer = setInterval(loop, 30);
Secondly, are you sure calling clearInterval does not clean the queue of pending loop() calls? If this is the case, you can easily disable these calls by using some sort of guard:
var done = false;
function loop() {
if(!done) {
// print something to screen
}
}
var timer = setInterval(loop(), 30);
canvas.onkeypress = function (event) {
clearInterval(timer);
done = true;
// print something else to screen
}

Categories