I've created this little object that's pretty handy when it comes to intervals and works pretty good as an animation frame, but it has this little thing about it. If the reference to the instance is lost, the interval keeps going.
function Interval(callback, interval){
var timer = null;
this.start = function(){
if(!timer) timer = setInterval(callback, interval);
};
this.stop = function(){
clearInterval(timer);
timer = null;
};
this.changeSpeed = function(a){
interval = a;
this.stop();
this.start();
}
this.destroy = function(){
delete this;
}
}
Obviously if javascript has no destruct method I can't track when to stop the interval, so I figured I should create a destroy method, but I'm not really sure if I can destroy the instance from within the object. It makes sense but... Any help is appreciated!
How about doing something like this:
function Interval(callback, interval){
var self = this;
var timer = null;
this.start = function(){
if(!timer) timer = setInterval(function() {
callback(self)
}, interval);
};
this.stop = function(){
clearInterval(timer);
timer = null;
};
this.changeSpeed = function(a){
interval = a;
this.stop();
this.start();
}
this.destroy = function(){
this.stop();
}
}
Now at least when the callback is called it will pass a reference to the your object and the callback will at least have a chance to stop it if they don't need it anymore.
The trick here is to use a closure to ensure you can still reference the object when the interval expires (hence the self variable).
So now I could do something like this:
var foo = new Interval(function(i) {
// check if my interval is still needed
if (dontNeedItAnymore) {
i.destroy(); // or just .stop()
}
else {
// do whatever
}
}, 1000);
foo = null; // whoops, lost the reference, but the callback will still be able to reference it
Related
I'm trying to cancel a requestAnimationFrame loop, but I can't do it because each time requestAnimationFrame is called, a new timer ID is returned, but I only have access to the return value of the first call to requestAnimationFrame.
Specifically, my code is like this, which I don't think is entirely uncommon:
function animate(elem) {
var step = function (timestamp) {
//Do some stuff here.
if (progressedTime < totalTime) {
return requestAnimationFrame(step); //This return value seems useless.
}
};
return requestAnimationFrame(step);
}
//Elsewhere in the code, not in the global namespace.
var timerId = animate(elem);
//A second or two later, before the animation is over.
cancelAnimationFrame(timerId); //Doesn't work!
Because all subsequent calls to requestAnimationFrame are within the step function, I don't have access to the returned timer ID in the event that I want to call cancelAnimationFrame.
Looking at the way Mozilla (and apparently others do it), it looks like they declare a global variable in their code (myReq in the Mozilla code), and then assign the return value of each call to requestAnimationFrame to that variable so that it can be used any time for cancelAnimationFrame.
Is there any way to do this without declaring a global variable?
Thank you.
It doesn't need to be a global variable; it just needs to have scope such that both animate and cancel can access it. I.e. you can encapsulate it. For example, something like this:
var Animation = function(elem) {
var timerID;
var step = function() {
// ...
timerID = requestAnimationFrame(step);
};
return {
start: function() {
timerID = requestAnimationFrame(step);
}
cancel: function() {
cancelAnimationFrame(timerID);
}
};
})();
var animation = new Animation(elem);
animation.start();
animation.cancel();
timerID; // error, not global.
EDIT: You don't need to code it every time - that's why we are doing programming, after all, to abstract stuff that repeats so we don't need to do it ourselves. :)
var Animation = function(step) {
var timerID;
var innerStep = function(timestamp) {
step(timestamp);
timerID = requestAnimationFrame(innerStep);
};
return {
start: function() {
timerID = requestAnimationFrame(innerStep);
}
cancel: function() {
cancelAnimationFrame(timerID);
}
};
})();
var animation1 = new Animation(function(timestamp) {
// do something with elem1
});
var animation2 = new Animation(function(timestamp) {
// do something with elem2
});
I'm trying to cancel a requestAnimationFrame loop, but I can't do it because each time requestAnimationFrame is called, a new timer ID is returned, but I only have access to the return value of the first call to requestAnimationFrame.
Specifically, my code is like this, which I don't think is entirely uncommon:
function animate(elem) {
var step = function (timestamp) {
//Do some stuff here.
if (progressedTime < totalTime) {
return requestAnimationFrame(step); //This return value seems useless.
}
};
return requestAnimationFrame(step);
}
//Elsewhere in the code, not in the global namespace.
var timerId = animate(elem);
//A second or two later, before the animation is over.
cancelAnimationFrame(timerId); //Doesn't work!
Because all subsequent calls to requestAnimationFrame are within the step function, I don't have access to the returned timer ID in the event that I want to call cancelAnimationFrame.
Looking at the way Mozilla (and apparently others do it), it looks like they declare a global variable in their code (myReq in the Mozilla code), and then assign the return value of each call to requestAnimationFrame to that variable so that it can be used any time for cancelAnimationFrame.
Is there any way to do this without declaring a global variable?
Thank you.
It doesn't need to be a global variable; it just needs to have scope such that both animate and cancel can access it. I.e. you can encapsulate it. For example, something like this:
var Animation = function(elem) {
var timerID;
var step = function() {
// ...
timerID = requestAnimationFrame(step);
};
return {
start: function() {
timerID = requestAnimationFrame(step);
}
cancel: function() {
cancelAnimationFrame(timerID);
}
};
})();
var animation = new Animation(elem);
animation.start();
animation.cancel();
timerID; // error, not global.
EDIT: You don't need to code it every time - that's why we are doing programming, after all, to abstract stuff that repeats so we don't need to do it ourselves. :)
var Animation = function(step) {
var timerID;
var innerStep = function(timestamp) {
step(timestamp);
timerID = requestAnimationFrame(innerStep);
};
return {
start: function() {
timerID = requestAnimationFrame(innerStep);
}
cancel: function() {
cancelAnimationFrame(timerID);
}
};
})();
var animation1 = new Animation(function(timestamp) {
// do something with elem1
});
var animation2 = new Animation(function(timestamp) {
// do something with elem2
});
I am having a hard time getting a countdown timer working as I don't know what I am doing wrong. I am trying to setup a countdown timer using jQuery in a prototype.
The main problem I see so far is at the setInterval:
_self.counter = setInterval(_self.runTimer(_self),1000);
When I don't pass in the "this" I get NaN but when I do the countdown only happens once and then stops.
Here is my JSFiddle work so far:
http://jsfiddle.net/f9GN7/
Thank you in advance.
I've modified a little of your code, I changed setInterval to setTimeout.
var timer_code = function(){
this.counter;
this.timeCountDown = 30;
}
timer_code.prototype = {
init : function(){
var _self = this;
$('#start').on('click',function(e){
_self.setTimer();
});
},
setTimer : function(){
var _self = this;
// _self.counter = setInterval(_self.runTimer(_self),1000);
var timerLoop = function(){
if(_self.timeCountDown > 0){
_self.runTimer();
setTimeout(timerLoop, 1000);
}
};
timerLoop();
},
runTimer : function(){
var _self = this;
_self.timeCountDown--;
if(_self.timeCountDown <= 0){
// clearInterval(_self.counter);
$('#timer').html("DONE");
return;
}
$('#timer').html(_self.timeCountDown);
console.log(_self.timeCountDown);
}
}
var timer = new timer_code();
timer.init();
http://jsfiddle.net/f9GN7/1/
setInterval gets a function reference as its first parameter ..
This function may not return a function object, the function call you just passed needs to be called in the scoope of a closure
Keeping your code with just a few modifications :
setTimer: function(){
if(this.counter)
clearInterval(this.counter); // timer may have already been launched, it may need to be cleared if its value is an integer and is != 0
this.counter = setInterval(
(function (ref) {
return function () {
ref.runTimer();
}
})(this),
1000);
}
See Fiddle Here
I have this function.
function changeFrame(){
var time = setInterval(start, 250);
}
and I want to stop it from firing in another function, but haven't been able to figure out how to do it.
Do you mean this?
function changeFrame(){
var time = setInterval(function() {
// Do stuff
}, 250);
}
Think it's in the comments.
Ok amended the fiddle to do what you want. I made time a global var. Call clearInterval in stop with the global var http://jsfiddle.net/QNWF4/3/
In order to call clearInterval you need to have the handle returned by setInterval. That means something will either be global to the page or global to a containing function in which your script resides.
function Timer()
{
var handle = null;
this.start = function (fn,interval) {
handle = setInterval(fn,interval);
};
this.stop = function ()
{
if (handle) { clearInterval(handle); handle = null; }
};
return this;
}
For instance, I am setting an interval like
timer = setInterval(fncName, 1000);
and if i go and do
clearInterval(timer);
it does clear the interval but is there a way to check that it cleared the interval? I've tried getting the value of it while it has an interval and when it doesn't but they both just seem to be numbers.
There is no direct way to do what you are looking for. Instead, you could set timer to false every time you call clearInterval:
// Start timer
var timer = setInterval(fncName, 1000);
// End timer
clearInterval(timer);
timer = false;
Now, timer will either be false or have a value at a given time, so you can simply check with
if (timer)
...
If you want to encapsulate this in a class:
function Interval(fn, time) {
var timer = false;
this.start = function () {
if (!this.isRunning())
timer = setInterval(fn, time);
};
this.stop = function () {
clearInterval(timer);
timer = false;
};
this.isRunning = function () {
return timer !== false;
};
}
var i = new Interval(fncName, 1000);
i.start();
if (i.isRunning())
// ...
i.stop();
The return values from setTimeout and setInterval are completely opaque values. You can't derive any meaning from them; the only use for them is to pass back to clearTimeout and clearInterval.
There is no function to test whether a value corresponds to an active timeout/interval, sorry! If you wanted a timer whose status you could check, you'd have to create your own wrapper functions that remembered what the set/clear state was.
I did this like below, My problem was solved. you should set the value like "false", when you clearTimeout the timer.
var timeer=false;
----
----
if(timeer==false)
{
starttimer();
}
-----
-----
function starttimer()
{
timeer_main=setInterval(activefunction, 1000);
timeer=true;
}
function pausetimer()
{
clearTimeout(timeer_main);
timeer=false;
}
Well you can do
var interval = setInterval(function() {}, 1000);
interval = clearInterval(interval);
if (typeof interval === 'undefined'){
...
}
but what are you actually trying to do? clearInterval function is an always success function and it will always return undefined even if you call it with a NaN value, no error checking in there.
You COULD override the setInterval method and add the capability to keep track of your intervals. Here is an untestet example to outline the idea. It will work on the current window only (if you have multiple, you could change this with the help of the prototype object) and this will only work if you override the functions BEFORE any functions that you care of keeping track about are registered:
var oldSetInterval = window.setInterval;
var oldClearInterval = window.clearInterval;
window.setInterval = function(func, time)
{
var id = oldSetInterval(func, time);
window.intervals.push(id);
return id;
}
window.intervals = [];
window.clearInterval = function(id)
{
for(int i = 0; i < window.setInterval.intervals; ++i)
if (window.setInterval.intervals[i] == id)
{
window.setInterval.intervals.splice(i, 1);
}
oldClearInterval(id);
}
window.isIntervalRegistered(id)
{
for(int i = 0; i < window.setInterval.intervals; ++i)
if (window.setInterval.intervals[i] == func)
return true;
return false;
}
var i = 0;
var refreshLoop = setInterval(function(){
i++;
}, 250);
if (isIntervalRegistered(refrshLoop)) alert('still registered');
else alert('not registered');
clearInterval(refreshLoop);
if (isIntervalRegistered(refrshLoop)) alert('still registered');
else alert('not registered');
The solution to this problem: Create a global counter that is incremented within your code performed by setInterval. Then before you recall setInterval, test if the counter is STILL incrementing. If so, your setInterval is still active. If not, you're good to go.