SuperTasche
SuperTasche

Reputation: 509

JavaScript Singleton losing reference

I have a problem with a singleton pattern that I have implemented in 'program.js':

var Program = (function() {

var _program; // Instance of program

// Constructor
function Program() {

    if (typeof _program != "undefined") {
        throw new Error("Program can only be instantiated once.");
    }

    this.run = false;   // flag to allow for program exec

    _program = this;
};

// Public methods
Program.prototype.init = function() {
    // the run call is nested in 4 callbacks
    callbackHell (
       this.run = true;
    );
};

Program.prototype.execute = function() {
    if(this.run == true) {
        // do stuff
    }
};

Program.getProgram = function () {
    if(typeof _program == "undefined")
    {
        return new this();
    }
    return _program;
};

// Return the constructor
return Program;
})();

In my 'main.js' I keep a reference to the loaded program and check for the run flag to allow execution. Looks like this:

var program = null;

function initDemo() {
    program = Program.getProgram();
    program.init();
    runDemo();
};

function runDemo() {
    if(program != null && program.run) {
        program.execute();
    }
    requestAnimationFrame(runDemo);
};

If I execute this code on a Chrome browser, it will never reach the program.execute() call in main.js. The reference of program will keep the run flag to false, even it is changed in the init() function. I checked for all of this in the debugger. I should point out that the 'this.run = true' call is nested in 4 callbacks. After a while I figured I could just change the run flag of the global reference of program in main.js with the init() function. So instead of 'this.run = true', 'program.run = true'. This works and the loop will run execute(). However this is not the style I am used from OOP. What is actually happening here? It definitely has to do with the callbacks: when I put 'this.run = true' out of the callbacks at the end of init() the flag is changed correctly, however at the wrong time of the program execution.

Upvotes: 1

Views: 144

Answers (1)

Borys Serebrov
Borys Serebrov

Reputation: 16172

Probably your callbacks in the callbackHell are doing something asynchronously and there is a delay before program.run will actually be set to true, the sequence is approximately this:

  • You call program.init()
  • Your callbacks start working
  • You call runDemo(), here program.run is false and it exists
  • Callbacks finish their work and program.run becomes true

The solution to that is to make your runDemo to be another callback, so your main code will look like this:

var program = null;

function initDemo() {
    program = Program.getProgram();
    program.init(runDemo);
};

function runDemo() {
    if(program != null) {
        program.execute();
    }
    requestAnimationFrame(runDemo);
};

Here you don't need the program.run flag at all, instead you just start your demo from inside the "callbackHell":

Program.prototype.init = function(functionToRun) {
    // the run call is nested in 4 callbacks
    callbackHell (
       functionToRun(); // instead of setting program.run 
                        // we call the specified function
    );
};

Upvotes: 1

Related Questions