Reputation: 3520
I've been struggling with this for days now... I just can't seem to get the callback functions to fire, and I assume it's because of a scope problem since the code works when I separate the "classes" and manually test all the functions etc.
I have a VisualTimer object
function VisualTimer(customConfig) {
// Create a new Timer object
var object = this;
this.timer = new Timer(object, function(data) { this.timerUpdated(data); }, function (data) { this.timerFinished(data); });
// Set default configuration
this.config = {
// The ID's of the timer's control elements
pauseButtonId : "pause-timer",
startButtonId : "start-timer",
stopButtonId : "stop-timer",
// The ID's of the timer's display elements
hoursDisplayId : "hours-left",
minutesDisplayId : "minutes-left",
secondsDisplayId : "seconds-left",
// The ID's of the timer's input elements
hoursInputId : "hours-left",
minutesInputId : "minutes-left",
secondsInputId : "seconds-left",
};
// Replace the default configuration if a custom config has been provided
if (typeof(customConfig) == "object") this.config = customConfig;
}
that serves like an extension to a Timer object, just to separate the underlying timer and the visual controls. As you can see I'm injecting the new Timer() "object" with the VisualTimer functions that should be called on each update. However, it doesn't work.
The Timer "object" is defined as follows
function Timer(callbackObject, updateCallback, finishedCallback) {
// Define timer variables
this.state = Timer.stateStopped;
this.callbackObject = (typeof(callbackObject) == "object") ? callbackObject : null;
this.updateCallback = (typeof(updateCallback) == "function") ? updateCallback : null;
this.finishedCallback = (typeof(finishedCallback) == "function") ? finishedCallback : null;
this.hours = 0;
this.minutes = 0;
this.seconds = 0;
// Save the values that the timer was initially set to
this.startHours = 0;
this.startMinutes = 0;
this.startSeconds = 0;
/**
* Store the setInterval() ID that is created when the timer is started
* so that we can use this to clear it (stop the timer) later on.
*/
this.interval = null;
}
with functions for starting, pausing, stopping, etc. and as you can see in the "constructor" I have variables for injecting callback functions that should be called after each tick
Timer.prototype.tick = function(fireCallback) {
fireCallback = (typeof(fireCallback) == "boolean") ? fireCallback : true;
if (this.seconds > 0) {
// If the seconds value is bigger than 0, subtract it by 1
this.seconds -= 1;
// Fire the update callback function
console.log(this.hours + ":" + this.minutes + ":" + this.seconds);
if (fireCallback && this.updateCallback != null) this.updateCallback.call(this.callbackObject, this.getCurrentValues());
}
else if (this.minutes > 0) {
// If the seconds value is 0 but not the minutes value, subtract it by 1
this.minutes -= 1;
// And then also update the seconds value
this.seconds = 59;
// Fire the update callback function
console.log(this.hours + ":" + this.minutes + ":" + this.seconds);
if (fireCallback && this.updateCallback != null) this.updateCallback.call(this.callbackObject, this.getCurrentValues());
}
else if (this.hours > 0) {
// If neither the seconds nor the minutes value is bigger than 0 but the hours value is, subtract it by 1
this.hours -= 1;
// And then also update the minutes and the seconds values
this.minutes = 59;
this.seconds = 59;
// Fire the update callback function
console.log(this.hours + ":" + this.minutes + ":" + this.seconds);
if (fireCallback && this.updateCallback != null) this.updateCallback.call(this.callbackObject, this.getCurrentValues());
}
else {
// Stop the timer
this.stop(false);
// Fire the finished callback function
console.log("The timer has finished.");
if (fireCallback && this.finishedCallback != null) this.finishedCallback.call(this.callbackObject, this.getInitialValues());
}
};
The ticking works perfectly fine, so there's nothing wrong in the code up until now, and the console.log() outputs correctly so the timer and the tick function works perfect. The callback function doesn't get fired, though.
I've done a lot of debugging, and all functions works perfectly so the problem is definitely in the actual calling of the injected callback functions. Everything else works as expected.
This is the updateCallback function that should be fired on each tick
VisualTimer.prototype.timerUpdated = function(timerData) {
if (typeof(timerData) == "object")
{
if (timerData.state == Timer.stateRunning) {
// Update the timer's display elements with new values
this.updateDisplayElements(timerData.hours, timerData.minutes, timerData.seconds);
}
else if (timerData.state == Timer.statePaused) {
// Make sure the correct values are displayed
this.updateDisplayElements(timerData.hours, timerData.minutes, timerData.seconds);
window.alert("The timer has been paused!");
}
else if (timerData.state == Timer.stateStopped) {
// Reset the timer's input and display elements
this.updateDisplayElements();
this.updateInputElements();
window.alert("The timer has been stopped!");
}
}
};
UPDATE: All the code is now available as a JSFiddle project.
2nd UPDATE:
Problem solved! The issue was that the state
property was never sent to the callback function due to the function that was supposed to be named getInitialValues()
was accidentally named getCurrentValues()
and therefore overwrote that function. getInitialValues()
doesn't send the state, but getCurrentValues()
do. After correcting the function names it all worked perfectly :)
Upvotes: 0
Views: 601
Reputation: 7244
You are not passing the state of the timer into the callback properly, so this code always no-ops
VisualTimer.prototype.timerUpdated = function(timerData) {
if (typeof(timerData) == "object")
{
if (timerData.state == Timer.stateRunning) { // THIS IS ALWAYS FALSE
// Update the timer's display elements with new values
this.updateDisplayElements(timerData.hours, timerData.minutes, timerData.seconds);
}
else if (timerData.state == Timer.statePaused) {
// Make sure the correct values are displayed
this.updateDisplayElements(timerData.hours, timerData.minutes, timerData.seconds);
window.alert("The timer has been paused!");
}
else if (timerData.state == Timer.stateStopped) {
// Reset the timer's input and display elements
this.updateDisplayElements();
this.updateInputElements();
window.alert("The timer has been stopped!");
}
}
};
because there is in fact no state
property on timerData
. Here is where you set the state. At this point the this
object is the Timer object itself
// Set the current state to running
this.state = Timer.stateRunning;
Then when you call the above noop code, you do:
if (fireCallback && this.updateCallback != null) this.updateCallback.call(this.callbackObject, this.getCurrentValues());
Which sets this
to this.callbackObject
which does not have a state
property.
The callbacks are getting called, just not properly.
Upvotes: 1