Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript: Callback in constructor

I'm trying to write OO javascript for an object that has an expensive initialization process that will callback a function when its done.

The problem is that the caller needs to use the functions of that same object in the callback routine, and the object doesn't exist yet:

// ctor for foo object
function foo(callback) {

    // do slow initialization here..

    // callback when done
    callback();
};

foo.prototype = function() {

    return {
        // doStuff method
        doStuff: function() {
          alert('stuff done');
        }
    };        
}();

// instantiate the foo object, passing in the callback
var f = new foo(function() {

    //Uncaught TypeError: Cannot call method 'doStuff' of undefined 
    f.doStuff();

});​

jsFiddle What am I missing here?

like image 269
saille Avatar asked Dec 22 '12 04:12

saille


3 Answers

It should be a simple fix. First, make sure your callback is called with the this object set to the current object

function foo(callback) {
    // do slow initialization here..

    callback.call(this);
};

Then adjust how you make your callback

var f = new foo(function() {
    this.doStuff();
});​

Here's your updated fiddle

like image 53
Adam Rackis Avatar answered Oct 12 '22 10:10

Adam Rackis


Here's why it doesn't work: when JavaScript is executing the code, before it can set f, it needs to evaluate the new foo(...) expression. Inside of the constructor, it calls the callback. JavaScript still has not set f since the expression has not yet finished. As soon as the constructor finishes, f is set correctly, but it never gets there because you try to use f inside of the callback when it is still undefined.


You could call the callback on the next iteration through the event loop, so rather than:

callback();

You'd do:

setTimeout(callback, 0);

This would only work if you're already assuming that it completes asynchronously, though.

like image 23
icktoofay Avatar answered Oct 12 '22 10:10

icktoofay


Your approach is the intrinsic issue. Define doStuff within the constructor. The call as such:

var f = new Foo();
f.doStuff();

You can also create an init method to run whenever a new object is created. But, you should do this within the scope of the constructor... do not pass a callback to a constructor.

like image 38
Joe Johnson Avatar answered Oct 12 '22 10:10

Joe Johnson