views:

383

answers:

6

Like others before me I'm struggling with scope in Javascript. (That and trying to read the darn stuff). I have checked some of the previous threads on this question but I cant seem to get them to apply correctly to my issuue. In the example below, I want to manipulate the values in the tagsArr array, once the array has been fully populated. I declared the tagsArr variable outside the scope of the function in which it is populated in order to access it globally. But the variable doesn't seem to have the scope I expect - tagsArr.length is 0 at the point where I call output it to console on line 16.

            $(function(){
            var apiKey = [myapikey];
            var tags = '';
            var tagsArr = new Array();
            $.getJSON('http://api.flickr.com/services/rest/?&method=flickr.people.getPublicPhotos&api_key=' + apiKey + '&user_id=46206266@N05&extras=date_taken,tags&format=json&jsoncallback=?', function(data){
                $.each(data.photos.photo, function(i, item) {
                    var photoID = item.id;
                    $.getJSON('http://api.flickr.com/services/rest/?&method=flickr.photos.getInfo&api_key=' + apiKey + '&photo_id=' + photoID + '&format=json&jsoncallback=?', function(data){
                        if (data.photo.tags.tag != '') {
                            $.each(data.photo.tags.tag, function(j, item) {
                                tagsArr.push(item.raw);
                            });
                        }                            
                    });
                    tags = tagsArr.join('<br />');
                    console.debug(tagsArr.length);
                });
                $('#total-dragged').append(data.photos.total);
                $('#types-dragged').append(tags);
            });
        });
A: 

You may want to check the answer to this question I posted. There is some good information on scope issues in javascript.

UmYeah
+2  A: 

$.getJSON is asynchronous (the a in ajax). That means that by the time you get to console.debug(), getJSON is still getting. You'll need to do some extra work in the JSON callback.

Skilldrick
Sidenote: OP *is* doing work in the callback. I think you meant you need to "move" some work *into* the callback.
JPot
I like to keep my lines short - I didn't notice there was a callback at the end of that line! Thanks.
Skilldrick
+1  A: 

This isn't a scope issue - the problem is that getJSON is asynchronous, so it continues executing immediately after sending the request to flickr. By the time the browser executes console.debug the request hasn't returned and you haven't finished handling the response (and therefore haven't pushed any items into the array yet).

To solve this, find all the code that should only be executed when the array is full and move it into your getJSON callback method:

if (data.photo.tags.tag != '') {
    $.each(data.photo.tags.tag, function(j, item) {
        tagsArr.push(item.raw);
    });
    tags = tagsArr.join('<br />');
    console.debug(tagsArr.length);
    $('#total-dragged').append(data.photos.total);
    $('#types-dragged').append(tags);
}
Jeff Sternal
+1  A: 

The reason for this is that getJSON is an asynchronous request. after the call to $.getJSON, the javascript engine will move immediately on to the following two lines of code, and will output the length of your array, which is by then, zero-length. Not until after that does the getJSON request receive a response, and add items to the array.

David Hedlund
+1  A: 

The getJSON function is asynchronous, so when you call the debug function the array is still empty because the requests are not completed. Use the $.ajax function and set async:false and it will work.

    $.ajax({
      type: "GET",
      url: 'http://api.flickr.com/services/rest/?&amp;method=flickr.photos.getInfo&amp;api_key=' + apiKey + '&photo_id=' + photoID + '&format=json&jsoncallback=?',
      dataType: "json",
      async:false,
      success:function(data){
         if (data.photo.tags.tag != '') {
               $.each(data.photo.tags.tag, function(j, item) {
                   tagsArr.push(item.raw);
               });
         } 
      }
    });
mck89
that's a terrible solution though.
David Hedlund
But that's A SOLUTION.
mck89
There should be a separate `$.jax` function :)
Skilldrick
+2  A: 

Your calls to getJSON are asynchronous. Hence all the calls to the inner getJSON will still be outstanding by the time the console.debug line is reached. Hence the array length is still 0.

You need to run some extra code once the final getJSON call has completed.

        $(function(){ 
        var apiKey = [myapikey]; 
        var tags = ''; 
        var tagsArr = new Array(); 
        $.getJSON('http://api.flickr.com/services/rest/?&amp;method=flickr.people.getPublicPhotos&amp;api_key=' + apiKey + '&user_id=46206266@N05&extras=date_taken,tags&format=json&jsoncallback=?', function(data){

            var totalExpected = data.photos.total;
            var totalFetched = 0;

            $.each(data.photos.photo, function(i, item) { 
                var photoID = item.id;

                $.getJSON('http://api.flickr.com/services/rest/?&amp;method=flickr.photos.getInfo&amp;api_key=' + apiKey + '&photo_id=' + photoID + '&format=json&jsoncallback=?', function(data){ 
                    if (data.photo.tags.tag != '') { 
                        $.each(data.photo.tags.tag, function(j, item) { 
                            tagsArr.push(item.raw);
                            totalFetched += 1;
                            if (totalFetched == totalExpected)
                              fetchComplete();
                        }); 
                    }                             
                });
                function fetchComplete()
                { 
                    tags = tagsArr.join('<br />'); 
                    console.debug(tagsArr.length); 
                }
            }); 
            $('#total-dragged').append(data.photos.total); 
            $('#types-dragged').append(tags); 
        }); 
    });

This works assuming the total number of photos doesn't excede the default 100 per page, other wise you would need to tweak it.

That said I don't think using .each to fire off loads of getJSON requests makes a great deal of sense. I would refactor it so that only one call to getJSON is outstanding at any one time. Have the callback of one issue the next getJSON for the next photo until all have been pulled then do your completed code.

AnthonyWJones
Thanks! This is a great solution. Incrementing the `totalFetched` variable (and the associated `if` clause) need to be moved one clause further down though, it's currently counting tags and not photos fetched!
fearoffours