matthew david matthew david - 2 months ago 7
HTML Question

I can console.log my array but it's values shows up as undefined when appended to a div

I can console.log the array before and after I step through it to build list items. When I run the code I get 10 list items in my html that all read "undefined" instead of being the values I'm pulling out of my chrome history.

Any ideas as to why?

var urlarray = []

var historyResults = function () {

var microsecondsPerWeek = 1000 * 60 * 60 * 24 * 7;
var oneWeekAgo = (new Date).getTime() - microsecondsPerWeek;

chrome.history.search({
'text': '', // Return every history item....
'startTime': oneWeekAgo // that was accessed less than one week ago.
}, function(historyItems) {
for (var i = 0; i < historyItems.length; ++i) {
urlarray.push(historyItems[i].url);
}
})
console.log(urlarray)

}

historyResults()


function addElement () {

var makeUL = function(data) {

var ul = document.createElement("ul");
// create the UL

console.log(urlarray)

for (i = 0; i < 10; i++) {
var a = document.createElement('a');
a.href = data[i];
a.appendChild(document.createTextNode(data[i]));
console.log(a)
var li = document.createElement("li");
li.appendChild(a);
ul.appendChild(li);
// step through the array and create a link out of the value of the array and append them to the list item
// append the list item to the UL
}

return ul;
// return ul full of li
}

console.log(urlarray)
document.getElementById("arraylist").appendChild(makeUL(urlarray));
// go to html to find div and append the full UL with urlarray as the array
}

addElement()

Amy Amy
Answer

You have two issues going on.

First, you are logging the array, but your browser does not log it immediately. It does so when it has the CPU available. When you log the array, its not yet populated with values. A moment later, when you expand the array in your browser console, the array is now populated because the evaluation of the array is delayed.

You can see this more clearly if you change your logging statement to: console.log(JSON.stringify(urlarray)). This forces the immediate evaluation of the object and turns it into a JSON string, which can then be written to the browser console a moment later.

For more information on the delayed evaluation of logged objects, see this question.

Okay, this brings us to your second issue. Your logging statement is executing before the callback to chrome.history.search does. That's why the array isn't yet populated. You need to use promises to ensure your code executes in the expected sequence. For this you should use a library like jQuery or Q.

I recommend reading about promises. Whichever library you use, your code will follow this basic structure:

  1. Get a 'deferred' object. I'll call it deferred.
  2. In your callback, resolve deferred with the array: deferred.resolve(urlarray)
  3. Where your logging statement is, get the promise from the deferred object. Return that promise from the historyResults method.
  4. Where you call historyResults, instead do:
historyResults.then(function(urls) { 
    console.log("promise was resolved!", urls);
    // do things with your urls here, like add elements
});

Do things that depend on your urls here, inside this callback. If you do, your code will be guaranteed to execute when the urls array is fully populated and ready to go.

This is a big topic, so google "javascript promises" and good luck. I hope this helps to get you started in the right direction.

If you don't want to use promises:

If you don't want to use promises, you will need to do everything inside the callback to chrome.history.search. That's the only way to guarantee the array is populated.

Asynchronous code is fun.

Comments