referencing function from setInterval using Javascript [duplicate] - javascript

This question already has answers here:
JavaScript setInterval and `this` solution
(9 answers)
Closed 7 years ago.
When I try and run the add function it gives TypeError: this.add is not a function
function Timer(){
this.t;
this.count = 0;
this.start = function(){
this.t = setInterval(function () {this.add()}, 1000);
}
this.add = function(){
this.count++;
console.log(this.count);
}
}
function startTimer(){
timer = new Timer();
timer.start();
}
How am I able to access this.add function in that instance?

A solution would be to create a variable to hold this but a simpler one is to use bind:
this.t = setInterval(this.add.bind(this), 1000);

This happens because this inside your anonymous function get wrong context. You need to bind that function to your original context:
this.t = setInterval(function () {this.add()}.bind(this), 1000);
Or keep reference to your context inside some variable:
function Timer(){
this.t;
this.count = 0;
this.start = function(){
var self = this;
this.t = setInterval(function () {self.add()}, 1000);
}
...

Related

How to you make a Javascript object's method call setInterval with an object method as an argument

I am trying to create a generic countdown timer object in Javascript
I have a method (decrementTimer) that reduces the timeLeft on counter by a fixed amount and another method which uses setInterval to call decrementTimer method.
The problem is that the code only runs once not every second. It looks like setInterval isn't putting the decrementTimer function on the queue inside my object.
I have tried making javascript do an Eval by putting the keyword "window" in front of the setIntervalfunction call but this doesn't work.
I can't use a Javascript class because I can't assume that all browsers support ECMAScript 6. I am using IE11.
I have also found solutions that work when you are doing this in a function but no examples of how to make this work in an object.
I would appreciate some help.
<script>
var myTimer = {
timeLeft: 10,
timerJobNumber: null,
decrementTimer: function(){
alert("decrementTimer called. timeLeft = " + this.timeLeft);
this.timeLeft = this.timeLeft - 1;
if(this.timeLeft<0){
alert("done");
}
},
startTimer: function(){
alert("startTimer called");
this.timerJobNumber = window.setInterval(this.decrementTimer(),10);
alert("Interval Job Number = " + this.timerJobNumber);
},
stopTimer: function(){
clearInterval(this.timerJobNumber);
alert(this.timerJobNumber);
alert("job terminated");
},
resetTimer: function(initialTime){
this.TimeLeft = initialTime;
alert("intitialTime="+intitialTime);
},
getTimeLeft: function(){
return this.timeLeft;
}
};
console.log(myTimer.getTimeLeft());
console.log(myTimer.startTimer() );
console.log(myTimer.getTimeLeft());
</script>
I didn't really check all of your code, but this seems to do what you want :
var myTimer = {
timeLeft: 10,
timerJobNumber: null,
decrementTimer: function(){
console.log("decrementTimer called. timeLeft = " + this.timeLeft);
this.timeLeft = this.timeLeft - 1;
if(this.timeLeft<0){
this.stopTimer();
}
},
startTimer: function(){
this.timerJobNumber = window.setInterval(this.decrementTimer.bind(this),1000);
console.log("Interval Job Number = " + this.timerJobNumber);
},
stopTimer: function(){
clearInterval(this.timerJobNumber);
alert(this.timerJobNumber);
},
resetTimer: function(initialTime){
this.TimeLeft = initialTime;
alert("intitialTime="+intitialTime);
},
getTimeLeft: function(){
return this.timeLeft;
}
};
Note that you can easily transform this into a class like function :
var MyTimer = function() {
this.timeLeft = 10;
this.timerJobNumber = null;
};
MyTimer.prototype.decrementTimer = function() {
console.log("decrementTimer called. timeLeft = " + this.timeLeft);
this.timeLeft = this.timeLeft - 1;
if(!this.timeLeft > 0)
this.stopTimer();
};
MyTimer.prototype.startTimer = function() {
this.timerJobNumber = window.setInterval(this.decrementTimer.bind(this),1000);
console.log("Interval Job Number = " + this.timerJobNumber);
};
MyTimer.prototype.stopTimer = function() {
clearInterval(this.timerJobNumber);
alert(this.timerJobNumber);
};
MyTimer.prototype.resetTimer = function(initialTime) {
this.timeLeft = initialTime;
alert("intitialTime="+intitialTime);
};
MyTimer.prototype.getTimeLeft = function() {
return this.timeLeft;
};
//...
var m = new MyTimer();
m.startTimer();
I think you don't bind your decrementTimer() inside the setInterval() function.
startTimer: function(){
alert("startTimer called");
this.timerJobNumber = window.setInterval(this.decrementTimer.bind(this),10);
alert("Interval Job Number = " + this.timerJobNumber);
}
if you are not familiar this topic, then follow this link. I think it will help you.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind

Can't call a method by using 'this' [duplicate]

This question already has answers here:
JavaScript setInterval and `this` solution
(9 answers)
Closed 8 years ago.
I'm trying to call my method Move(); inside the object MySnake using setInterval:
function Snake()
{
this.Start = function(Speed)
{
this.Movement = setInterval(function(){
this.Move();
},Speed);
}
}
var MySnake = new Snake();
MySnake.Start(400); //Doesn't work
and this isn't working. But when I call the method through the instance 'MySnake':
function Snake()
{
MySnake.Start = function(Speed)
{
this.Movement = setInterval(function(){
MySnake.Move();
},Speed);
}
}
var MySnake = new Snake();
MySnake.Start(400); //Works
I wan't the one whit 'this' keyword to work
This is because this is defined by the caller in JavaScript. The easiest solution is to store it in another variable:
function Snake()
{
this.Start = function(Speed)
{
var that = this;
this.Movement = setInterval(function(){
that.Move();
},Speed);
}
}
var MySnake = new Snake();
MySnake.Start(400); //Work
Here is a working jsfiddle. In your example, the inner this is the global window.
Another solution would be to bind this in the function to the local this, as shown in this second jsfiddle:
function Snake()
{
this.Move = function() { document.body.innerHTML += '.'; };
this.Start = function(Speed)
{
this.Movement = setInterval((function(){
this.Move();
}).bind(this),Speed);
}
}
var MySnake = new Snake();
MySnake.Start(400); //Work
But this one is harder to read.
when you do this.move(); "this" is inside anonymous function passed into the setInterval method, hence you will get an error.
function Snake()
{
this.Start = function(Speed)
{
var _this = this;
this.Movement = setInterval(function(){
_this.Move();
},Speed);
}
}
var MySnake = new Snake();
MySnake.Start(400)
This will work since the reference to the object is captured by closure created by the callback for setInterval.

Clear interval from within a closure

I'm trying to clear an interval when the user hovers over an element and then start it up again when they hover off an element. I think this is a closure but I'm not sure, hopefully my code will make sense what I'm trying to do.
var rotatorInterval = function(elem){
var interval = setInterval(function(){
var active = elem.find('.dot.active');
if(active.is('.dot:last-of-type',elem)){
elem.find('.dot').first().click();
}else{
active.next().click();
}
},6000);
interval;
return interval;
};
if($('.rotator').length){
$('.rotator').each(function(){
var self = $(this);
rotatorInterval(self);
self.find('.slide, .dot').on('mouseenter',function(){
console.log('hovered');
clearInterval(interval);
});
});
}
I tried returning the interval from that closure but when I hovered it said interval (the name of the variable I returned) is not defined, so it's like it didn't return it or something.
You just have to actually return the interval reference somewhere
var rotatorInterval = function (elem) {
var interval = setInterval(function () {
var active = elem.find('.dot.active');
if (active.is('.dot:last-of-type', elem)) {
elem.find('.dot').first().click();
} else {
active.next().click();
}
}, 6000);
return interval;
};
if ($('.rotator').length) {
$('.rotator').each(function () {
var self = $(this);
var return_interval = rotatorInterval(self);
self.find('.slide, .dot').on('mouseenter', function () {
clearInterval(return_interval);
});
});
}

Prevent event inside object function to overwrite "this"

Game.prototype.run = function() {
window.setInterval(function() {
var thisLoop = new Date().getTime();
this.update();
this.render();
lastLoop = thisLoop;
}, 1000 / this.fps);
};
game.js:198Uncaught TypeError: Object [object DOMWindow] has no method 'update'
Why is this happening ?
"this" should relate to the Game object.
Cache the this variable, or use Function.bind:
Game.prototype.run = function() {
var _this = this;
window.setInterval(function() {
var thisLoop = new Date().getTime();
_this.update();
_this.render();
lastLoop = thisLoop;
}, 1000 / this.fps);
};
Or, using Function.bind:
Game.prototype.run = function() {
window.setInterval((function() {
...
}.bind(this), 1000 / this.fps);
};
this in a function passed to setInterval refers to the global window object, or is undefined (in strict mode).
Another method, similar to the first one. Pass this as a parameter to the function (so that no extra local variable is used):
Game.prototype.run = function() {
window.setInterval(function(_this) {
var thisLoop = new Date().getTime();
_this.update();
_this.render();
lastLoop = thisLoop;
}, 1000 / this.fps, this);
};
No, this in the scope of the function refers to the function itself. Its somewhat hard to wrap your head around scoping in JS if you're not used to it.
The easy solution is to cache the context of "this" outside the anonymous function and use that instead.
Game.prototype.run = function() {
var game = this;
window.setInterval(function() {
var thisLoop = new Date().getTime();
game.update();
game.render();
lastLoop = thisLoop;
}, 1000 / this.fps);
};
DEMO: http://jsfiddle.net/kmendes/awzMn/
In this case there's no way you can do that because the setInterval function has a different scope. This is what you can do:
Game.prototype.run = function() {
var currentGame = this;
window.setInterval(function() {
var thisLoop = new Date().getTime();
currentGame.update();
currentGame.render();
lastLoop = thisLoop;
}, 1000 / this.fps);
};
Try this :
Game.prototype.run = function() {
var intervalCallBack = function() {
var thisLoop = new Date().getTime();
this.update();
this.render();
lastLoop = thisLoop;
};
var self = this;
window.setInterval(function(){intervalCallBack.call(self);}, 1000 / this.fps);
};
Because of the fact that setInterval and setTimeout executes your callback in the global context, your this "pointer" that you used to refer to your game object is now referring to the global object (window) , which of course has no method 'update'.

How to stop setInterval?

<span id="ccc">10</span> <span id="start">start</span> <span id="stop">stop</span>
$('#start').click(function(){
var c = $('#ccc').text();
var inter = setInterval(function() {
c--;
$('#ccc').text(c);
}, 1000);
});
$('#stop').click(function(){
clearInterval(inter);
});
how i must rewrite this for correctly use STOP?
LIVE: http://jsfiddle.net/c3hZh/
inter needs to be in-scope for both functions. Wrap both functions with a closure so that you can avoid polluting the global namespace with a new variable.
(function ($) {
var inter;
$('#start').click(function(){
var c;
c = parseInt($('#ccc').text()); //make sure you're getting a numeric value
//don't forget to clear any existing interval before setting a new one:
if (inter) {
clearInterval(inter);
}
inter = setInterval(function() {
c--;
$('#ccc').text(c);
}, 1000);
});
$('#stop').click(function() {
clearInterval(inter);
});
}(jQuery));
inter is a local variable.
It doesn't exist outside your callback.
You need to use a global variable.
var inter = 0;
$('#start').click(function(){
var c = $('#ccc').text();
inter = setInterval(function() {
c--;
$('#ccc').text(c);
}, 1000);
});
$('#stop').click(function(){
clearInterval(inter);
});

Categories