I'm trying to create a self contained timer where all variables are inside a object.
For the most part it works but I can get this to fire once. What am I missing?
function Countdown()
{
this.appId = null;
this.timerId = null;
this.seconds = null;
this.decrementCounter = function (instant)
{
if (instant == null)
return;
instant.tick();
if (instant.seconds === 0)
{
instant.tickEnd();
instant.stop();
}
instant.seconds--;
};
this.tick = function ()
{
var xx = this.appId
};
this.tickEnd = function ()
{
var xx = this.appId
};
this.start = function ()
{
clearInterval(this.timerId);
this.timerId = setInterval(this.decrementCounter(this), 1000);
};
this.stop = function ()
{
clearInterval(this.timerId);
};
}
I modified your code a bit and changed the line containing setInterval
to this:
this.timerId = setInterval((function(scope) {return function() {scope.decrementCounter(scope);};})(this), 1000);
The functions run inside of setInterval
run in the window
scope. It only runs once, because you don't pass the function itself just the result of it. You need to return the actual function or pass an anonymous function which calls it.
jsfiddle demo: http://jsfiddle.net/2gLdL/1/
You are calling the function, not assigning a reference
this.timerId = setInterval(this.decrementCounter(this), 1000);
Seems weird you are passing in "this" as an argument...
use bind()
this.timerId = setInterval(this.decrementCounter.bind(this, this), 1000);
or a closure
var that = this;
this.timerId = setInterval(function(){that.decrementCounter(that); }, 1000);
In the following bit of code:
this.timerId = setInterval(this.decrementCounter(this), 1000);
You are executing this immediately:
this.decrementCounter(this)
And so the return value of that, is what is being called by setInterval each second. Usually you would want to pass a function closure, like this:
var _this = this; //make sure your variable is available in the scope that follows
this.timerId = setInterval(function() { this.decrementCounter(_this); }, 1000);
Then, when your timer is executed, it's the function which is called, and that function then executes what you want. The other option is to pass the first parameter as a string such as "callAFunction('blah')" which will be evaluated each second and executed, but I believe the above is that you want.
Try this:
setInterval(function(){Countdown()},500);
I provided an answer to another question that creates a simple stopwatch.
You could modify the code slightly to count down instead of up.
Here's a jsbin demo.
Original snippet
var Stopwatch = function(elem, options) {
var timer = createTimer(),
startButton = createButton("start", start),
stopButton = createButton("stop", stop),
resetButton = createButton("reset", reset),
offset,
clock,
interval;
// default options
options = options || {};
options.delay = options.delay || 1;
// append elements
elem.appendChild(timer);
elem.appendChild(startButton);
elem.appendChild(stopButton);
elem.appendChild(resetButton);
// initialize
reset();
// private functions
function createTimer() {
return document.createElement("span");
}
function createButton(action, handler) {
var a = document.createElement("a");
a.href = "#" + action;
a.innerHTML = action;
a.addEventListener("click", function(event) {
handler();
event.preventDefault();
});
return a;
}
function start() {
if (!interval) {
offset = Date.now();
interval = setInterval(update, options.delay);
}
}
function stop() {
if (interval) {
clearInterval(interval);
interval = null;
}
}
function reset() {
clock = 0;
render();
}
function update() {
clock += delta();
render();
}
function render() {
timer.innerHTML = clock/1000;
}
function delta() {
var now = Date.now(),
d = now - offset;
offset = now;
return d;
}
// public API
this.start = start;
this.stop = stop;
this.reset = reset;
};