user1804588
user1804588

Reputation: 21

Accessing Object properties when the object is within an array (javascript)

I am attempting to create an array of objects and then access object properties within the array, but it comes back undefined. I call the createObjArray() function and immediately after I do a console.log(objArray[1]); and it prints out the object with all it...s properties just fine. However, if I attempt to do console.log(objArray[1].name); firebug prints "undefined". Also, when stepping through my code in firebug I can mouse over objArray[1].name and it displays the correct name. What is happening here, it's driving me nuts.

var objArray = [];

function createObjectArray(numOfObjs) {

    for(var i=0; i<numOfObjs; i++) {

packages.push(initObj(i)); 

    }
 }

function initObj(i){
    var newPackage;
    var p = {};
    $.getJSON('.../package' + i + '.json', function(data) {
        newPackage = new Package(data);
        p.name = newPackage.name;
        p.id = i;      
    });
    return p;
 }

Upvotes: 2

Views: 109

Answers (2)

SGr
SGr

Reputation: 853

This will work:

var objArray = [];

function createObjectArray(numOfObjs, callback) {
    var filledPackage = [];
    var nbLeft = numOfObjs;
    for(var i=0; i<numOfObjs; i++) {
        initObj(i, function(p){
            filledPackage.push(p);
            nbLeft--;
            if (nbLeft === 0){
                callback(filledPackage);
            }
        }); 
    }
 }

function initObj(i, callback){
    var newPackage;
    var p = {};
    $.getJSON('.../package' + i + '.json', function(data) {
        newPackage = new Package(data);
        p.name = newPackage.name;
        p.id = i;     
        callback(p);
    });
}

//Get a filled object array:
createObjectArray(5, function(filledArray){
    objArray = filledArray;
    //Code here will be executed AFTER all the $.getJSON queries have returned.
    //objArray is not empty.
});
//Code here will be executed WHILE the getJSON queries are running and
//while objArray is still empty. Due to the way the JS event loop works,
//it is impossible that code placed here will be able to use the content
//of objArray unless you call an async function such as anything AJAX or
//setTimeout, but that's iffy. Code you want to be executed once objArray
//has been filled should be inside of the callback above.

The problem is that $.getJSON is aynchronous, meaning that it doesn't automatically returns a result. Instead, you give it a callback. A callback is a function to execute once it has received a result. In this case, the callback is the anonymous function created when calling $.getJSON. That callback receives the result from the server, adds it to the array and then checks if the array has been filled. Since we're doing async code due to the $.getJSON function, we must return the result asynchronously too. To do so, we demand the initObj function to receive a function to call once it has completed (another callback). We call that callback and pass it the parameter. We then return the filled array through a callback once again.

Upvotes: 1

James Gaunt
James Gaunt

Reputation: 14783

Your call to $.getJSON is asynchronous. When initObj() returns p it is still an empty object.

However initObj() creates a closure which captures a reference to p so when $.getJSON returns p is populated.

This is why the object seems empty in code you run immediately after populating the array. However by the time you run your console command the asynchronous calls have returned and the objects are populated.

You need to wait for all your async calls to return before continuing work on the array. One way to do this would be to increment a counter when you make a call and decrement it when a call returns, then when the final call returns the counter would drop to zero and you continue processing.

Alternatively you could setup a setTimout loop to keep polling the array the check when all its items are populated.

Both approaches are risky if you think one of the calls might fail, but the approach itself is fundamentally risky as you are making multiple ajax calls so you have to handle multiple possible failures. It would be a lot cleaner to grab all the data in one go so you can handle success / error states once in the success / error handler in jQuery.ajax.

Upvotes: 0

Related Questions