Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing object literal properties from inside callbacks (async methods)

I'm writing a chrome extension which needs to interact with a subtree of bookmarks. There are a lot of interactions with this subtree, so I'm abstracting this logic into an object literal, like so:

var contextStore = {
    'root_id': undefined,
    'setup': function() {...},      // populates root_id
    'add': function(name) {...},    // uses root_id
    'remove': function(name) {...}, // uses root_id
    // ... etc ...
};

contextStore.setup(); // only once.
contextStore.add("foo");
contextStore.add("bar");
// ... etc

So far, so good.

The trouble I'm having is caused by the asynchronous Chrome APIs (and my lack of JS-fu). To wit:

var contextStore = {
    'root_id': undefined,
    'setup': function() {
        chrome.bookmarks.getTree(function(tree) {
           // do some work to find a given folder in bookmarks.
           // now I want to save that folder's id for access in other methods.

           // Fail: 'this' refers to chrome.bookmarks.getTree. 
           this.root_id = computed_thing; // doesn't work!
        });
    }
    // ... etc ...
};

My question is:

How do I go about accessing members of the enclosing object literal from inside the various Chrome API method callbacks?

I looked at using the module pattern, but it doesn't seem to change things, and it's not like this code is going to be consumed by anything outside the extension.

like image 860
Idan Gazit Avatar asked Nov 08 '11 09:11

Idan Gazit


2 Answers

You need to store a reference to the this which points to the contextStore object;

var contextStore = {
    'root_id': undefined,
    'setup': function() {
        var that = this; // Store reference here.

        chrome.bookmarks.getTree(function(tree) { 
           that.root_id = computed_thing; // does work!
        });
    }
    // ... etc ...
};

This is equivalent to doing;

var contextStore = {
    'root_id': undefined,
    'setup': function() {
        chrome.bookmarks.getTree(function(tree) { 
           contextStore.root_id = computed_thing; // does work!
        });
    }
    // ... etc ...
};

However you gain the benefit of not reusing contextStore everywhere.

like image 164
Matt Avatar answered Nov 15 '22 20:11

Matt


The this keyword can be bound to different things depending on how you call it. I'm not a javascript expert, but there is a good explanation at A List Apart.

The solution is to bind explicitly when calling the function using either my_function.apply(obj, [args]), my_function.call(obj, args) (call now) or pre-bind the function for calling later: my_function.bind(obj).

As a python programmer, being explicit probably makes you happy :-)

Matt's answer is eventually the better approach, as it is even more explicit, succinct and doesn't require the function to be called or prepared in a certain way. I just thought I would try to explain what was going on.

like image 40
Will Hardy Avatar answered Nov 15 '22 20:11

Will Hardy