Best way to have event occur n times? - javascript

I use the following code to create countdowns in Javascript. n is the number of times to repeat, freq is the number of milliseconds to wait before executing, funN is a function to call on each iteration (typically a function that updates part of the DOM) and funDone is the function to call when the countdown is complete.
function timer(n, freq, funN, funDone)
{
if(n == 0){
funDone();
}else{
setTimeout(function(){funN(n-1); timer(n-1, freq, funN, funDone);}, freq);
}
}
It can be called like so:
timer(10,
1000, /* 1 second */
function(n){console.log("(A) Counting: "+n);},
function() {console.log("(A) Done!");}
);
timer(10,
500,
function(n){console.log("(B) Counting: "+n);},
function() {console.log("(B) Done!");}
);
The advantage of this is that I can call timer() as many times as I want without worrying about global variables etc. Is there a better way to do this? Is there a clean way to make setInterval stop after a certain number of calls (without using global variables)? This code also creates a new lambda function with each call to setTimeout which seems like it could be problematic for large countdowns (I'm not sure how javascript's garbage collector handles this).
Is there a better way to do this? Thanks.

This is basically the same idea as #balabaster, but it is tested, uses prototype, and has a little more flexible interface.
var CountDownTimer = function(callback,n,interval) {
this.initialize(callback,n,interval);
}
CountDownTimer.prototype = {
_times : 0,
_interval: 1000,
_callback: null,
constructor: CountDownTimer,
initialize: function(callback,n,interval) {
this._callback = callback;
this.setTimes(n);
this.setInterval(interval);
},
setTimes: function(n) {
if (n)
this._times = n
else
this._times = 0;
},
setInterval: function(interval) {
if (interval)
this._interval = interval
else
this._interval = 1000;
},
start: function() {
this._handleExpiration(this,this._times);
},
_handleExpiration: function(timer,counter) {
if (counter > 0) {
if (timer._callback) timer._callback(counter);
setTimeout( function() {
timer._handleExpiration(timer,counter-1);
},
timer._interval
);
}
}
};
var timer = new CountDownTimer(function(i) { alert(i); },10);
...
<input type='button' value='Start Timer' onclick='timer.start();' />

I'd create an object that receives a counter and receives a function pointer to execute, something akin to the following pseudo code:
TimedIteration = function(interval, iterations, methodToRun, completedMethod){
var counter = iterations;
var timerElapsed = methodToRun; //Link to timedMethod() method
var completed = callbackMethod;
onTimerElapsed = function(){
if (timerElapsed != null)
timerElapsed();
}
onComplete = function(){
if (completed != null)
completed();
}
timedMethod = function(){
if (counter != null)
if (counter > 0) {
setTimeOut(interval, onTimerElapsed);
counter--;
}
else
onComplete();
this = null;
}
}
if ((counter != null)&&(counter > 0)){
//Trip the initial iteration...
setTimeOut(interval, timedMethod);
counter--;
}
}
obviously this is pseudo code, I've not tested it in an IDE and syntactically I'm not sure if it'll work as is [I'd be astonished if it does], but basically what you're doing is you're creating a wrapper object that receives a time interval, a number of iterations and a method to run upon the timer elapsed.
You'd then call this on your method to run like so:
function myMethod(){
doSomething();
}
function doWhenComplete(){
doSomethingElse();
}
new TimedIteration(1000, 10, myMethod, doWhenComplete);

I like your original solution better than the proposed alternatives, so I just changed it to not create a new function for every iteration (and the argument of fun() is now the value before decrement - change if needed...)
function timer(n, delay, fun, callback) {
setTimeout(
function() {
fun(n);
if(n-- > 0) setTimeout(arguments.callee, delay);
else if(callback) callback();
},
delay);
}

Related

How to exec a function after previous ends?

guys. It's a timer. I wanna run the timer and when it's end do something else(like a warning),and then run again with other amount of minutes. But I can't cause always only the second call is executed:
$(document).ready(function() {
timer(5,timer(25));
// timer(5);
// timer(25); do not work... only exec de last one
});
function timer(countTo,callback){
var time = 10; /* how long the timer runs for */
var initialOffset = '440';
var i = 1
var interval = setInterval(function() {
$('.circle_animation').css('stroke-dashoffset', initialOffset-(i*(initialOffset/countTo)));
$('h2').text(i);
if (i == countTo) {
clearInterval(interval);
}
i++;
}, 1000);
callback();
}
Which is the best solution? There is something that I am not understanding... Thanks anyway!
Well, first off:
timer(5,timer(25));
If you think this line will execute timer(5), and then at the end of timer(5) it will execute timer(25), you are mistaken. This is actually going to evaluate timer(25) immediately, and pass its return value (undefined) as the second parameter to timer(5,undefined).
If you intended to pass that as a callback, you need to pass a function. So you could do:
timer(5,timer.bind(null,25));
But, for that matter, you don't even check if callback exists before attempting to invoke it, so you probably are getting a reference error anyway.
timer(5,timer(25));
starts two timers and passes the result of the second (undefined) to the first as callback. You want:
timer(5,timer.bind(window,25));
And the callback needs to be executed if i==countTo ...
Is this what you want?
timer(5,function(){timer(25)});
Your problem is here:
timer(5,timer(25));
You should type
timer(5, function(){
timer(25)
});
//or using ES6 syntax
timer(5, () => timer(25));
because timer(25) returns its value (this function doesn't return value so it tries to invoke undefined), not that function.
Also read about closures, it might be helpful.
Instead of runing a callback(), you need to run the function itself (timer()). You'll also need to run a for loop inside your function that checks how many times the function has already run. If it reaches your desired maximum, break out of that. This way it won't run indefinitely.
In the following example, the timer() function executes five times, which is what I'm assuming you want by calling timer(5).
$(document).ready(function() {
timer(5);
});
function timer(countTo) {
for (var iterations = 0; iterations < countTo; iterations++) {
var time = 10; /* how long the timer runs for */
var initialOffset = '440';
var i = 1
var interval = setInterval(function() {
$('.circle_animation').css('stroke-dashoffset', initialOffset - (i * (initialOffset / countTo)));
$('h2').text(i);
if (i == countTo) {
clearInterval(interval);
}
}, 1000);
timer();
console.log("Iteration:", iterations + 1);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
To run the function 25 times after this, all you have to do is call timer(25) directly after timer(5):
$(document).ready(function() {
timer(5);
timer(25);
});
$(document).ready(function() {
timer(5);
timer(25);
});
function timer(countTo) {
for (var iterations = 0; iterations < countTo; iterations++) {
var time = 10; /* how long the timer runs for */
var initialOffset = '440';
var i = 1
var interval = setInterval(function() {
$('.circle_animation').css('stroke-dashoffset', initialOffset - (i * (initialOffset / countTo)));
$('h2').text(i);
if (i == countTo) {
clearInterval(interval);
}
}, 1000);
timer();
console.log("Iteration:", iterations + 1);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Hope this helps! :)

recursive setTimeout - not a variable

I want to make a timer that counts down from some initial number and stops when it gets to zero.
I originally did this with setInterval, but I wanted to separate the timer (setInterval) from the count down function, and was finding it difficult to terminate the setInterval.
I'm currently trying to achieve the same thing with a setTimeout which conditionally calls the same setTimeout again, but it doesn't work.
function Timer(initialTime) {
this.time = initialTime;
this.tickTock = null
}
Timer.prototype.countDown = function() {
if (this.time <= 0) {
clearTimeout(this.tickTock);
} else {
console.log(this.time);
this.time--;
this.proceed();
}
}
Timer.prototype.proceed = function() {
this.tickTock = setTimeout(this.countDown, 3000);
}
var timer = new Timer(10);
timer.proceed();
When calling timer.proceed(), I'm getting error:
TypeError: this.proceed is not a function
at Timer.countDown [as _onTimeout]
How can I refer to the proceed function from within the countDown function?
The callback to setTimeout is not bound to your object but it's bound to window thus this is the window objet not your timer object. You can bind the callback using Function.prototype.bind like this:
this.tickTock = setTimeout(this.countDown.bind(this), 3000);
Note: when using setTimeout there will be no need for this.tickTock, you can stop the counting down by not calling another proceed. You can keep it but it will be of no use. (see the code snippet bellow).
Working code snippet:
function Timer(initialTime) {
this.time = initialTime;
}
Timer.prototype.countDown = function() {
if (this.time <= 0) { // if the counter is less or equal 0, return and don't call proceed
return;
}
// otherwise continue
console.log(this.time);
this.time--;
this.proceed();
}
Timer.prototype.proceed = function() {
setTimeout(this.countDown.bind(this), 1000);
}
var timer = new Timer(10);
timer.proceed();

How to disable a function when time passed?

I'm trying to disable 2 functions when a certain time period is reached and enable the other 2 after that time period. So the second 2 functions would have to be disabled to begin with.
I was thinking of using the following code to wrap around the functions:
Code:
var startTime = new Date().getTime();
var interval = setInterval(function(){
if(new Date().getTime() - startTime > 5000){
clearInterval(interval);
return;
}
function 1() {}
$(function 2() {});
}, 1000);
function 3() {}
$(function 4() {});
Can you help?
If you want to control whether functions do something or not, based on how much time has elapsed, it would probably be easier to set a flag after the interval you need, and then have your functions check that flag to decide if they are going to do something:
var timedOut = false;
setTimeout(function () {
timedOut = true;
}, 5000);
function one() {
if (!timedOut) {
// do something
}
}
function two() {
if (!timedOut) {
// do something
}
}
function three() {
if (timedOut) {
// do something
}
}
function four() {
if (timedOut) {
// do something
}
}
This should get you started; I've simply redefined the original func1/func2 functions after a set time (5 seconds, as your example uses). This could do any number of things (such as remove the function definition altogether).
(function(document,window,undefined){
// Used simply to show output to the window.
var db = document.getElementById('db');
// Here we define the initial state of our two functions.
// Nothing magical here, just outputting a description.
window.func1 = function(){
db.innerHTML += 'Hello from original func1\r\n';
}
window.func2 = function(){
db.innerHTML += 'Hello from original func2\r\n';
}
// Here we keep the same format you used (using the Date to
// define when one's been deprecated over the other).
var startTime = new Date().getTime(),
interval = setInterval(function(){
var currentTime = new Date().getTime(),
delta = currentTime - startTime;
if (delta > 5000){
// In here, now that the specified amount of time has
// elapsed, we redefine the meaning of the two original
// functions. We could also simply remove them.
window.func1 = function(){
db.innerHTML += 'Hello from NEW func1\r\n';
}
window.func2 = function(){
db.innerHTML += 'Hello from NEW func2\r\n';
}
clearInterval(interval);
}
}, 1000);
})(document,window);
// This is here just to show you how one definition is changed
// in place of another.
setInterval(function(){
func1();
func2();
}, 1000);
<pre id="db"></pre>
If you mean 'disabling' the functions after certain amount of seconds then this should do the trick.
var secondsLimit = 10,
a = 0,
b = setInterval(function () { a += 1; }, 1000 });
function A() {
if (a > secondsLimit) {
return;
}
// do stuff
}
You can change the functions if you call them e.g. by a global variable scope.
In the following example based on your code, the functions switch after 4 seconds.
var function1 = function() {
console.log("function 1 active");
};
var function2 = function() {
console.log("function 2 active")
}
var startTime = new Date().getTime();
setTimeout(function() {
function1 = function() {
console.log("now function 3 is active instead of function 1");
}
function2 = function() {
console.log("now function 4 is active instead of function 2");
}
}, 4000);
//the following code is just for testing reasons
var interval = setInterval(function() {
function1();
function2();
}, 1000)

Javascript: Force new loop iteration in setInterval

I have a setInterval loop. It's set to 3500 milliseconds, like so:-
var loop = setInterval(function() { /*stuff*/ }, 3500);
At one point in 'stuff' if a certain situation occurs, I want to force a new iteration of the loop and NOT WAIT for the 3500 milliseconds. How is that possible? Is it continue or do I just need to frame the process differently?
You could try writing an anonymous self-calling function using setTimeout instead of setInterval:
var i = 0;
(function() {
// stuff
i++;
if (i % 2 == 0) {
// If some condition occurs inside the function, then call itself once again
// immediately
arguments.callee();
} else {
// otherwise call itself in 3 and a half seconds
window.setTimeout(arguments.callee, 3500);
}
})();​ // <-- call-itself immediately to start the iteration
UPDATE:
Due to a disagreement expressed in the comments section against the usage of arguments.callee, here's how the same could be achieved using a named function:
var i = 0;
var doStuff = function() {
// stuff
i++;
if (i % 2 == 0) {
// If some condition occurs inside the function, then call itself once again
// immediately
doStuff();
} else {
// otherwise call itself in 3 and a half seconds
window.setTimeout(doStuff, 3500);
}
};
doStuff();
You can use something like this... using setTimeout instead of setInterval...
<script type="text/javascript">
var show;
var done = false;
show = setTimeout(showHideForm, 3500);
function showHideForm() {
// Do something
if(done) {
clearTimeout(show);
show = setTimeout(showHideForm, 2000);
}
}
</script>
clearTimeout takes as argument the handle which is returned by setTimeout.
Use a named function and call it when you want.
var loop = setInterval(loopFunc, 3500);
function loopFunc(){
//do something
}
function anticipate(){
clearInterval(loop); //Stop interval
loopFunc(); //Call your function
loop = setInterval(loopFunc, 3500); //Reset the interval if you want
}
My contrived example:
var time = 3500,
loops = 0,
loop;
(function run(){
var wait = time,
dontwait = false;
if (loops++ == 5) {
loops = 0;
dontwait = 1000;
}
console.log('Interval: ', dontwait || wait);
return loop = setTimeout(run, dontwait || wait);
})();​
http://jsfiddle.net/NX43d/1/
Basically, a self-invoking function looping back on a self-calling function, with (!) shorthand variable switching. Nifty.
function looper(t) {
var loop = setInterval(function() {
document.write(s++);
if (mycondition) { // here is your condition
loopagain(200); // specify the time which new loop will do
loop = window.clearInterval(loop); // clear the first interval
return; // exit from this function!
}
}, t);
}
window.onload = looper(1000); // this will have default setInterval function time ans will start at window load!
function loopagain(t) {
looper(t);
}​
http://jsfiddle.net/tFCZP/

Add a timeout to "setInterval()"?

I have a setInterval function like below on a Divbox so if I leave a divbox, this setInterval is triggered:
setInterval("playthis()", 1000);
What I want it to do: If I leave the divbox and lets say within the next 2 second rehover it, the setInterval should not triggered.
Is this possible?
You can use cousins setTimeout and clearTimeout to set a function callback that invokes your setInterval only after 2 uninterrupted seconds:
var handle = null;
function yourDivboxLeaveHandler() {
handle = setTimeout(startPlay, 2000);
}
function yourDivboxHoverHandler() {
if (handle !== null) {
clearTimeout(handle);
handle = null;
}
}
function startPlay() {
setInterval(playthis, 1000); // use function references please, not eval
handle = null;
}
You will want much better variable/function names than this though.
Yes. Just make some creative use of clearInterval().
In other words, no, such a feature doesn't come out-of-the-box, but you can build it yourself by calling clearInterval() if the mouse re-enters the divbox before the interval is triggered.
For example:
var divBox = document.getElementById('MyDivBox');
var TimeoutHandle = null;
divBox.onmouseover = function()
{
if ( TimeoutHandle != null )
{
clearTimeout(TimeoutHandle);
}
}
divBox.onmouseout = function()
{
TimeoutHandle = setTimeout(function()
{
TimeoutHandle = null;
setInterval(playthis, 1000);
}, 2000);
}
First of all is a bad practice to have the code evalued in a setInterval so you should avid double quotes. Then you can clear the interval like this:
var int = setInterval(playthis, 1000);
clearInterval(int)

Categories

Resources