Tom
Tom

Reputation: 3520

Callback functions not getting called

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

Answers (1)

unobf
unobf

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

Related Questions