views:

72

answers:

1

Hi,

I'm working on a Google Chrome extension. In the popup I have the following code:

var bookmarks = [];
function appendBMTnode(node){
    bookmarks.push([node[0].title, node[0].id]);
}
function addchildren(results){
    for(x = 0; x < results.length; x++){
        bookmarks.push([results[x].title, results[x].id]);
        chrome.bookmarks.getChildren(results[x].id, addchildren);
    }
}
function getallbookmarks(){
    chrome.bookmarks.get('0', appendBMTnode);
    chrome.bookmarks.getChildren('0', addchildren);
}
getallbookmarks();
console.debug(bookmarks.length);
console.debug(bookmarks);

Now, I would assume that the first command would issue the # of bookmarks I have. Indeed, when I use Chrome's debugger and add bookmarks.length to the watch list, 418 is the value. In the console of the debugger I can write bookmarks.length and it will give me the correct length. I can type

for(x = 0; x < bookmarks.length; x++){ console.debug(bookmarks[x]); }

and I get string representations of each inner array. However, that original console.debug(bookmarks.length) gives an output of zero. And if I add console.debug(bookmarks[0]); to the popup.html it tells me that the value is undefined.

But if I add the following to getallbookmarks() (either first or last):

for(x = 0; x < 10; x++){
    bookmarks.push(x);
}

then bookmarks.length will be 10 at first, and then 428. Also when I add the following function:

function printlen(){
    console.debug(bookmarks.length);
}

and then in the body if I add

<a href="#" onclick="printlen()">test</a>

then I'll also get the proper bookmarks.length value.

Any clue why the bookmarks objects won't register?

+6  A: 

chrome.bookmarks.get is an asynchronous function. When you call it, it returns straight away, loading the bookmark data in the background.

So if you look at bookmarks.length immediately after the call to getallbookmarks(), it will naturally be incomplete. When you later click on the button to printlen(), the bookmark loading will have finished by then and the array will be filled.

You can't know your bookmarks array is complete and ready for use until both appendBMTnode() and addchildren() have been called. That's why the chrome.bookmark methods take callback functions instead of just returning a value.

[edit]:

how do I "wait" until it's finished loading everything?

You can't really, JavaScript doesn't give you the language-level tools (like threads or co-routines) to run asynchronous code synchronously or vice versa. You'll have to get used to writing asynchronous code based on callback functions.

In this case you could have a counter that you increased every time you called get() or getChildren(), and decremented at the end of each appendBMTnode and addchildren callback. If the counter reaches zero at that point, there are no asynchronous calls waiting, so you can call a ‘completion’ callback function of your own. Callback functions are often written as inline function expressions to make the flow clearer.

An issue with the code at the moment is that there is no inherent order to the bookmarks array, since everything is called in parallel. It it not guaranteed that get() will return a result before getChildren(), and each time addchildren calls getChildren() again, the results for the different parents could come in in any order.

Consider using the getTree() method instead. This will give you the whole bookmarks list in one go, which will probably be considerably easier to handle:

var bookmarks= [];
chrome.bookmarks.getTree(function(marks) {
    for (var i= 0; i<marks.length; i++)
        bookmarks.push([marks[i].title, marks[i].id]);
    // bookmarks is now populated. do code that relies on bookmarks here
});
// at this point bookmarks will still be empty, the callback function hasn't
// happened yet
bobince
So here's another question - how do I "wait" until it's finished loading everything?
Wayne Werner
@WayneWerner: added.
bobince