I am working on a countdown timer written in Javascript. Fairly basic really. Just uses setInterval for the timing aspect. I wrote it using the prototype method of storing functions and variables so I can create a "class".
I call the code in this fashion.
function testTimer() {
var newTimer = new CDTimer($("#voteTimer"),30,"");
newTimer.start();
}
When the below code runs, console.log is printing out undefined or NaN.
function CDTimer (target, duration, callback) {
this.target = target;
this.duration = duration;
this.callback = callback;
}
CDTimer.prototype.start = function() {
this.start = new Date().getTime();
this.interval = setInterval(this.update, 1000);
}
CDTimer.prototype.update = function() {
console.log(this.duration, this.start);
this.elapsed = this.duration - (new Date().getTime() - this.start) / 1000
if (this.elapsed < 0) {
clearInterval(this.interval);
this.callback();
}
else {
console.log(this.elapsed);
$(this.target).text(this.elapsed);
}
}
CDTimer.prototype.stop = function() {
clearInterval(this.interval);
}
I must be missing something silly. What is happening to my variables and their values?
Thanks for the insight.
The function called from setInterval is provided a this which is the window, not the timer.
You may do this :
CDTimer.prototype.start = function() {
this.start = new Date().getTime();
var _this = this;
this.interval = setInterval(function(){_this.update()}, 1000);
}
Note that the MDN offers a detailed explanation.
EDIT following comment : if you don't want to create a new variable in the start function, you could do this :
CDTimer.prototype.start = function() {
this.start = new Date().getTime();
this.interval = setInterval(function(_this){_this.update()}, 1000, this);
}
But I'm not sure the readibility is improved by this move of the variable creation and it's not compatible with IE (if you don't patch it, see MDN's solution).
Related
I'm trying to make a timer for this game, that will count down from 30 seconds every 1000 Milliseconds, but when I run the code, the Timer does change; it stays at 30.
Here is the code I am using:
var gameTimer = {
time: 30,
interval: undefined,
start: function(time) {
var self = this;
this.interval = setInterval(tick, 1000);
},
tick: function() {
this.time = this.time - 1;
},
stop: function(time){
clearInterval(this.interval);
},
reset: function(){
this.time = 30;
}
};
Can someone please help me out?
Do it like this instead
var gameTimer = function() {
this.time = 30;
this.interval;
this.start = function() {
self = this;
this.interval = setInterval(this.tick.bind(this), 1000);
console.log(this.interval);
};
this.tick = function() {
console.log(this.time);
this.time = this.time - 1;
};
this.stop = function(){
clearInterval(this.interval);
};
this.reset = function(){
this.time = 30;
};
};
var timer = new gameTimer();
timer.start();
Fiddle demo http://jsfiddle.net/jL2jc4jr/
In the code example I didn't put the code stopper but I think you can do that from this point on. Enjoy you game timer.
Change from this:
this.interval = setInterval(tick, 1000);
to this:
this.interval = setInterval(this.tick.bind(this), 1000);
The issue is that when you pass tick to setInterval, the object reference is lost when tick() is called and thus this has the wrong value. You probably end up changing a global variable named time because of the wrong this value.
Passing this.tick.bind(this) causes the this pointer to be set properly when tick() is called by the interval and thus the this.time reference in that method works properly.
FYI, if you were working in strict mode, your error would have been pointed out to you by the runtime because this would have been undefined rather than window (one of the reasons for strict mode).
So I've already written a function that works (based on underscores throttle) for functions that don't take in a parameter, but I'd like to make it generic enough to pass in a function with a variable number of parameters. Here's what I have:
(function () {
var lastTime = new Date().getTime();
function foo() {
var newTime = new Date().getTime();
var gap = newTime - lastTime; // Travels up scope chain to use parents lastTime. Function has access to variables declared in the same scope
console.log('foo called, gap:' + gap);
lastTime = newTime; // Updates lastTime
//console.log(x);
//x++;
}
var throttle = function(func, wait) {
var result;
var timeout = null; // flag updated through closure
var previous = 0; // time last run updated through closure
return function() { //func, wait, timeout, previous available through scope
var now = new Date().getTime();
var remaining = wait - (now - previous);
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(this, arguments); //func is available through closure
}
return result;
};
};
document.addEventListener("scroll", throttle(foo, 1000));
//document.addEventListener("scroll", throttle(foo(5), 2000));
}());
But I'd like to modify foo to foo(x) and get this to work
(function () {
var lastTime = new Date().getTime();
function foo(x) {
var newTime = new Date().getTime();
var gap = newTime - lastTime; // Travels up scope chain to use parents lastTime. Function has access to variables declared in the same scope
console.log('foo called, gap:' + gap);
lastTime = newTime; // Updates lastTime
console.log(x);
x++;
}
var throttle = function(func, wait) {
var result;
var timeout = null; // flag updated through closure
var previous = 0; // time last run updated through closure
return function() { //func, wait, timeout, previous available through scope
var now = new Date().getTime();
var remaining = wait - (now - previous);
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(this, arguments); //func is available through closure
}
return result;
};
};
document.addEventListener("scroll", throttle(foo(5), 2000));
}());
throttle(foo(5), 2000)
Will not work because when you call a function with the parenthesis, you are invoking the function.
You would need to wrap foo in an anonymous function when passing it to throttle.
throttle(function(){
foo(5)
}, 2000)
If you wanted to keep track of the original value of x. wrap foo in a function and return foo. trapping the value in closure scope.
function foo(x) {
return function(){
var newTime = new Date().getTime();
var gap = newTime - lastTime; // Travels up scope chain to use parents lastTime. Function has access to variables declared in the same scope
console.log('foo called, gap:' + gap);
lastTime = newTime; // Updates lastTime
console.log(x);
x++;
}
}
Then you actually can us throttle(foo(5), 2000) because it does not execute the intended function on the first call.
Example here: http://repl.it/XOj/5 (fixed example)
A solution that takes in any number of args, modifies throttle:
function throttle(func, wait, args){
//do throttle stuff
func.apply(null, args);
}
Then throttle(foo(5), 2000) becomes throttle(foo, 2000, [5])
this file
http://www.iguanademos.com/Jare/docs/html5/Lessons/Lesson2/js/GameLoopManager.js
taken from this site
Here is the code:
// ----------------------------------------
// GameLoopManager
// By Javier Arevalo
var GameLoopManager = new function() {
this.lastTime = 0;
this.gameTick = null;
this.prevElapsed = 0;
this.prevElapsed2 = 0;
I understand the declaration of variables,
and they are used to record the time between frames.
this.run = function(gameTick) {
var prevTick = this.gameTick;
this.gameTick = gameTick;
if (this.lastTime == 0)
{
// Once started, the loop never stops.
// But this function is called to change tick functions.
// Avoid requesting multiple frames per frame.
var bindThis = this;
requestAnimationFrame(function() { bindThis.tick(); } );
this.lastTime = 0;
}
}
I don't understand why he uses var bindThis = this
this.stop = function() {
this.run(null);
}
This function set's gameTick to null, breaking the loop in this.tick function.
this.tick = function () {
if (this.gameTick != null)
{
var bindThis = this;
requestAnimationFrame(function() { bindThis.tick(); } );
}
else
{
this.lastTime = 0;
return;
}
var timeNow = Date.now();
var elapsed = timeNow - this.lastTime;
if (elapsed > 0)
{
if (this.lastTime != 0)
{
if (elapsed > 1000) // Cap max elapsed time to 1 second to avoid death spiral
elapsed = 1000;
// Hackish fps smoothing
var smoothElapsed = (elapsed + this.prevElapsed + this.prevElapsed2)/3;
this.gameTick(0.001*smoothElapsed);
this.prevElapsed2 = this.prevElapsed;
this.prevElapsed = elapsed;
}
this.lastTime = timeNow;
}
}
}
Most of this code is what I don't understand, I can see he is recording the time elapsed between frames, but the rest of the code is lost to me.
On the website he uses the term singleton, which is used to prevent the program trying to update the same frame twice?
I have a bit of experience with the javascript syntax, but the concepts of singleton, and the general goal/function of this file is lost to me.
Why is the above code needed instead of just calling
requestAnimationFrame(function() {} );
The reason he uses bindThis is that he is passing a method into an anonymous function on the next line. If he merely used this.tick(), this would be defined as the context of requestAnimationFrame. He could achieve the same thing by using call or apply.
Singletons are classes that are only instantiated once. This is a matter of practice, and not a matter of syntax - javascript doesn't know what a singleton is. By calling it a "Singleton", he is merely communicating that this is a class that is instantiated only once, and everything that needs it will reference the same instance.
How to call a function 10 times like
for(x=0; x<10; x++) callfunction();
but with 1 sec between each call?
function callNTimes(func, num, delay) {
if (!num) return;
func();
setTimeout(function() { callNTimes(func, num - 1, delay); }, delay);
}
callNTimes(callfunction, 10, 1000);
EDIT: The function basically says: make a call of the passed function, then after a bit, do it again 9 more times.
You can use setInterval for repeated execution with intervals and then clearInterval after 10 invocations:
callfunction();
var callCount = 1;
var repeater = setInterval(function () {
if (callCount < 10) {
callfunction();
callCount += 1;
} else {
clearInterval(repeater);
}
}, 1000);
Added: But if you don't know how long it takes your callfunction to execute and the accurate timings between invocation starting points are not important it seems it's better to use setTimeout for reasons mentioned by Paul S and those described in this article.
Another solution
for(var x=0; x<10; x++) window.setTimeout(callfunction, 1000 * x);
You can try to use setInterval and use a variable to count up to 10. Try this:
var number = 1;
function oneSecond () {
if(number <= 10) {
// execute code here..
number++;
}
};
Now use the setInterval:
setInterval(oneSecond, 1000);
Similar to Amadan's answer but with a different style of closure which means you re-use instead of creating new functions
function call(fn, /* ms */ every, /* int */ times) {
var repeater = function () {
fn();
if (--times) window.setTimeout(repeater, every);
};
repeater(); // start loop
}
// use it
var i = 0;
call(function () {console.log(++i);}, 1e3, 10); // 1e3 is 1 second
// 1 to 10 gets logged over 10 seconds
In this example, if you were to set times to either 0 or Infinity, it would run forever.
I don't know if there's a proper name, but I use a repeater:
function Repeater(callback, delay, count) {
var self = this;
this.timer = setTimeout(function() {self.run();},delay);
this.callback = callback;
this.delay = delay;
this.timesLeft = count;
this.lastCalled = new Date().getTime();
}
Repeater.prototype.run = function() {
var self = this;
this.timesLeft--;
this.callback();
this.lastCalled = new Date().getTime();
if( this.timesLeft > 0) {
this.timer = setTimeout(function() {self.run();},this.delay);
}
}
Repeater.prototype.changeDelay = function(newdelay) {
var self = this;
clearTimeout(this.timer);
this.timer = setTimeout(function() {self.run();},
newdelay-new Date().getTime()+lastcalled);
this.delay = newdelay;
}
Repeater.prototype.changeCount = function(newcount) {
var self = this;
if( this.timesLeft == 0) {
this.timer = setTimeout(function() {self.run();},this.delay);
}
this.timesLeft = newcount;
if( this.timesLeft == 0) clearTimeout(this.timer);
}
You can then use it like this:
new Repeater(callfunction, 1000, 10); // 1 second delay, 10 times
const functionCounterTimer = (callCount) => {
if (callCount < 10) {
setTimeout(() => {
++callCount
console.log("Function Call ", callCount);
functionCounterTimer(callCount);
}, 1000);
}
}
functionCounterTimer(0);
The above was my approach to a similar question.
setInterval(function(){},1000);
Calls the function for every second...
You can also use setTimeout for your thing to work.
I have a timer function and I want to clear the timeouts or reset the function, cause every time I execute it, a new timeouts are created, so I recieve several counts.
My idea is to reset the count every time I execute the function. I only want a 1 instance of timer and get the correct count. If if execute several times the function I want to restart to 0.
Here is my code:
var timeouts = new Array();
var timer = null;
io.sockets.on('connection', function (client)
{
client.on("start", function (){
console.log('Someone has pressed Start button',new Date().getTime());
//try to kill all timeouts
for (var timeout in timeouts) {
clearTimeout(timeout);
};
if(this.timer == null) {
this.timer = new timer(1000, function (data) {
io.sockets.emit('timeupdate', data);
})
}else {
this.timer = null;
});
});
function timer(delay, callback)
{
// self-reference
var self = this;
if (!(this instanceof timer)) {
return new timer();
}
// attributes
var counter = 0;
var start = new Date().getTime();
/**
* Delayed running of the callback.
*/
function delayed()
{
console.log(counter);
callback(counter);
counter ++;
var diff = (new Date().getTime() - start) - counter * delay;
var timeOut = setTimeout(delayed, delay - diff);
timeouts.push(timeOut);
}
// start timer
delayed();
var timeout = setTimeout(delayed, delay);
timeouts.push(timeout);
}
Thank you in advance.
Using clearTimeout() is the correct way. The problem is your for-loop. This might look like a classic foreach-loop, but it is not. You have to do:
for (var i=0; i< timeouts.length; i++) {
clearTimeout(timeouts[i]);
}
Alternatively, also I don't like this personally:
for (var i in timeouts) {
clearTimeout(timeouts[i]); // note how the array is indexed using var i
}
This is a common JavaScript pitfall - the for (x in y)-loop actually iterates over the array's indices, not the values. It can also iterate over an object's properties. Try it out:
var a = [3, 2, 5, 8];
for (var i in a) {
console.log(i);
console.log(a[i]);
}
var o = { test: 'hello', number: 1234 };
for (var x in o)
console.log(x);