I know a million JavaScript timer questions have been asked already, but I can't figure this one out.
The #drive element is updated the first time (to 0) but not after that, as if either setTimeout() is not working or this.count++ is not working. Anyone know why this isn't working?
Thanks...
var timer = {
timerRunning: false,
count: 0,
delay: 1000,
tick: function() {
$("#drive").html(this.count);
this.count++;
setTimeout(function(){
if (this.timerRunning)
this.tick();
}, this.delay);
},
start: function() {
this.timerRunning = true;
this.tick();
},
stop: function() {
this.timerRunning = false;
}
};
timer.start();
The value of this inside your setTimeout() callback is no longer your timer object (it will be the window object). That's why it isn't working.
You can fix it like this by setting a closure variable to your object and using that instead:
tick: function() {
$("#drive").html(this.count);
this.count++;
var self = this;
setTimeout(function(){
if (self.timerRunning)
self.tick();
}, this.delay);
},
The callback is bound to a different this variable. Here is a workaround:
tick: function() {
var self = this;
$("#drive").html(this.count);
this.count++;
setTimeout(function(){
if (self.timerRunning)
self.tick();
}, this.delay);
}
Related
I was playing with this example timer and then wanted to see if I could capture/pass data into the timer so I started with a simple message. If I remove the message to timer it ticks accordingly. I started logging other lifecycle methods but I am curious if this is JS thing which I don't think it is versus something in react life cycle. I forked a JS bin here with ex
'code'
var Timer = React.createClass({
getInitialState: function() {
return {secondsElapsed: 0};
},
tick: function(msg) {
console.log('msg is',msg);
this.setState({secondsElapsed: this.state.secondsElapsed + 1});
},
componentDidMount: function() {
this.interval = setInterval(this.tick('hi'), 1000);
},
componentWillUnmount: function() {
clearInterval(this.interval);
},
render: function() {
return (
<div>Seconds Elapsed: {this.state.secondsElapsed}</div>
);
}
});
React.render(<Timer />, document.getElementById("content"));
All you need to do is to change this bit in your code:
this.interval = setInterval(() => this.tick('hi'), 1000);
Alternatively, you can also send in this to the callback:
this.interval = setInterval(function(t) {t.tick('hi')}, 1000, this);
See the updated fiddle, here.
setInterval() takes (at least) two parameters, the first one is the callback and needs to be a function. You had provided the code directly; that code must be inside a function.
var intervalID = scope.setInterval(func, delay);
func
A function to be executed every delay milliseconds. The function is not passed any parameters, and no return value is expected.
Change code to:
componentDidMount: function() {
this.interval = setInterval(()=>this.tick('hi'), 1000);
}
I am using the below functions to start and stop spin.Basically I am trying to add an Autospin button and tried below approach but its not working.Start function is working but stop not working.
var nIntervId;
this._onAutoSpin = function(){
s_oGame.onSpin();
nIntervId = setInterval(this._onAutoSpin, 10 * 1000);
};
this._offAutoSpin = function(){
clearInterval(nIntervId);
};
The issue is because you're recursively starting a new interval every time it fires, therefore you only stop the latest timer, not the previous.
To fix this change your logic so that there is no possible recursion and there is only ever one interval running:
function Foo() {
var nIntervId;
this._onAutoSpin = function() {
nIntervId = setInterval(this._doAutoSpin, 1 * 1000); // modified for demo
}
this._doAutoSpin = function() {
console.log('spinning...');
};
this._offAutoSpin = function() {
console.log('stopped');
clearInterval(nIntervId);
};
}
var foo = new Foo();
foo._onAutoSpin();
setTimeout(foo._offAutoSpin, 5000); // stop after 5 seconds
If you want to do this recursively then you need to use setTimeout(). You also need to cache the this reference so that it is maintained within the successive calls:
function Foo() {
var nIntervId;
this._onAutoSpin = function() {
var _this = this;
console.log('spinning...');
nIntervId = setTimeout(function() {
_this._onAutoSpin();
}, 1 * 1000); // modified for demo
}
this._offAutoSpin = function() {
console.log('stopped');
clearInterval(nIntervId);
};
}
var foo = new Foo();
foo._onAutoSpin();
setTimeout(foo._offAutoSpin, 5000); // stop after 5 seconds
try to use setTimeout instead of setInterval because setInterval creates a new timer every time without destroying the last one.
var nIntervId;
this._onAutoSpin = function(){
s_oGame.onSpin();
nIntervId = setTimeout(this._onAutoSpin, 10 * 1000);
};
this._offAutoSpin = function(){
clearTimeout(nIntervId);
};
I'm trying to make a timer in javascirpt and jQuery using the setInterval function.
The timer should count down from 90 to zero (seconds).
The code that I'm using for this:
setInterval(settime(), 1000);
in this settime() sets the var time (started on 90) -1, this action has to happen once every second.
My problem is that I don't get how to let this action happen 90 times; I tried using a for loop but then the counter counts from 90 to 0 in 1 second.
Does anyone knows a better alternative?
Something like this should do the trick:
var count = 90;
var interval = setInterval(function(){
setTime();
if (count === 0){
clearInterval(interval); // Stopping the counter when reaching 0.
}
}, 1000);
I don't have the code you need but I'm sure you'll need to update the count at some point on your page.
You can cancel an interval with clearInterval which needs the ID of the interval that's created when you call setInterval
function timer(seconds, cb) {
var remaningTime = seconds;
window.setTimeout(function() {
cb();
console.log(remaningTime);
if (remaningTime > 0) {
timer(remaningTime - 1, cb);
}
}, 1000);
}
var callback = function() {
console.log('callback');
};
timer(90, callback);
Caution in using setInterval, may not work as you expect http://bonsaiden.github.io/JavaScript-Garden/#other.timeouts
setInterval keeps calling your function at each second (since you use 1000).
So setInterval expects a function as its first argument, which is the function which will be called periodically. But instead of passing settime, you pass its returned value. That won't work, unless settime returns a function.
Instead, try
setInterval(settime, 1e3);
Try utilizing .data() , .queue() , animate() , .promise() ; to stop "countdown" can call $(element).clearQueue("countdown")
var counter = function counter(el) {
return $(el || window).data({
"start": {
"count": 0
},
"stop": {
"count": 1
},
"countdown": $.map(Array(90), function(val, key) {
return key
}).reverse(),
"res": null
})
.queue("countdown", $.map($(this).data("countdown")
, function(now, index) {
return function(next) {
var el = $(this);
$($(this).data("start"))
.animate($(this).data("stop"), 1000, function() {
el.data("res", now)
$("pre").text(el.data("res"));
next()
});
}
})
)
.promise("countdown")
.then(function() {
$("pre").text($(this).data("res"))
.prevAll("span").text("countdown complete, count:");
});
};
$("button").on("click", function() {
if ($(this).is("#start")) {
counter();
$("pre").text(90).prev("span").html("");
$(window).dequeue("countdown");
}
else {
$(window).clearQueue("countdown").promise("countdown")
.then(function() {
$("pre").prevAll("span").html(function(_, html) {
return html.replace("complete", "stopped")
});
})
}
});
pre {
font-size:36px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<button id="start">start</button><button id="stop">stop</button>
<br />
<span></span>
<br />
<pre>90</pre>
Ok, so basically i'm creating an Interval class to handle repeating actions.
I have something like this:
function Interval(fn, speed) {
this.fn = fn;
this.speed = speed;
this.nt = setInterval(fn, speed);
}
And then i have 3 methods:
this.pause = function() {
clearInterval(this.nt);
}
this.start = function() {
this.nt = setInterval(this.fn, this.speed);
return this.nt;
}
this.wait = function(time) {
this.pause();
setTimeout(function() {
this.start();
}, time);
}
The problem appears in the third method. this.pause(); and this.start(); works as expected. But when I nest this.start into a setTimeout function it stops working. I don't understand why. Here's an example:
var i = 0:
var nt = new Interval(function() {
alert('Itineration: '+ i );
if(i>5);
nt.pause();
setTimeout(nt.start, 2000);
// nt.wait(2000);
}, 500);
Neither nt.wait(2000); nor nt.pause(); setTimeout(nt.start, 2000); is working.
this inside the timeout handler is not the Interval object, it is referring to the window object(not strict mode) so this.start() will not work
One solution is to pass a custom context using Function.bind()
this.wait = function (time) {
this.pause();
setTimeout(function () {
this.start();
}.bind(this), time);
// setTimeout(this.start.bind(this), time) as #elclanrs suggested
}
You are running into a context issue with your code. When the setTimeout function executes your callback the definition of "this" is no longer your Interval object. You need to modify your code so that you maintain a proper reference to the Interval object.
this.wait = function(time) {
var interval = this;
interval.pause();
setTimeout(function() {
interval.start();
}, time);
}
Edit
I just saw the other answer using .bind which is a much cleaner solution from a readability standpoint. One important note about .bind is that behind the scenes it basically generates another function to call your original function using the .call or .apply methods to set the correct value of this
In most cases the readability gained from using .bind is worth it. However, if this is going to be a core component to a larger system, it is a good idea to squeeze every ounce of performance you can out of it. Which would be an argument for avoiding .bind in this specific situation.
Working example based on the other answers.
function Interval(fn, speed) {
this.fn = fn;
this.speed = speed;
this.nt = setInterval(fn, speed);
this.pause = function () {
clearInterval(this.nt);
}
this.start = function () {
this.nt = setInterval(this.fn, this.speed);
return this.nt;
}
this.wait = function (time) {
this.pause();
setTimeout(function () {
this.start();
}.bind(this), time);
}
}
var i = 0;
var nt = new Interval(function () {
document.write('<pre>Itineration: ' + i + '</pre>');
i++;
nt.wait(2000);
}, 500);
How can I use "call" with "setInterval" to get an object literal to invoke one of its own methods?
Here's an example.
This works, and I understand why it works.
The timer object calls its own tick method once each second
var timer =
{
start: function()
{
var self = this;
setInterval(function(){self.tick();}, 1000);
},
tick: function()
{
console.log("tick!");
}
};
timer.start();
I tried to simplify this code by using "call".
This next example is the best that I came up with.
But it doesn't work: the tick method is called only once, and then I get a type error.
var timer =
{
start: function()
{
setTimeout.call(this, this.tick(), 1000);
},
tick: function()
{
console.log("tick!");
}
};
timer.start();
I think I don't really understand how call works.
Can anyone explain what I'm doing wrong?
You are .calling .setInterval not your callback function which the browser calls:
setInterval( this.tick.bind(this), 1000 );
Should work. See .bind
This is what I ended up with:
var timer = {
time: 0,
start: function() {
var timerTick = this.tick.bind(this);
window.setInterval(function() {
timerTick();
}, 1000);
},
tick: function() {
this.time += 1;
console.log(this.time);
}
};
timer.start();