Skip to content Skip to sidebar Skip to footer

Ajax And Variable Scope

I am writing a script that reads multiple twitter feeds and writes them to a div. The code works when I am just making 1 AJAX request to a specific feed, but when I try to loop thr

Solution 1:

I'm going in the opposite direction of the previous answer. You don't want the tweetArray to be global.

First of all, if you're getting nothing, then you probably have a syntax error in your code and you need to find that because the multi-user code should give you too much (duplicate tweets) not nothing. Since this is pseudo code, not your real code, we can't really help with where the syntax error is. Because the syntax error is likely in the getJSON success function handler, you probably won't see the syntax error directly in the debugger. To see where it is, you will need to put a breakpoint at the beginning of the callback and step through it to see where it goes nuts or put a try/catch inside your callback and output some debugging info if it throws an exception.

Besides a potential syntax error, the problem I see with your second block of code is that you're sharing tweetArray among all the success handlers, but then in each success handler, you're iterating across the whole tweetArray and appending the results. That means that the first JSON call will work because it will collect the initial tweets into the array and then append them all. The second JSON call will add to the same array and then iterate over the entire array and append them all. That will insert the tweets from the first call again - not something you want. The third call will again duplicate all the tweets that came before it. The solution to that part of the problem is to put the tweetArray inside the success function so you are only collecting and then inserting tweets from that particular JSON call.

To get each tweet appended exactly once, I think you need to change it like this:

functiongetTweets()
{
    var users = newArray();
    var user1 = [twitter name];
    var user2 = [twitter name]; //etc for each user
    users.push(user1,user2,...);

    for(var n=0;n<users.length;n++)
    {
        $.getJSON("http://api.twitter.com/1/statuses/user_timeline.json?screen_name="+users[n]+"&count=10&include_rts=true&callback=?",
        function(tweets){
            var thisUsersTweets = [];   // initialize this locally and separately for each success handler
            $.each(tweets, function(i,item){
                var tweet = item.text;
                var htmlString = ...;
                thisUsersTweets.push(htmlString);   // collect the tweets for this user only
            });

            for(var i=0;i<thisUsersTweets.length;i++)
            {
                $("#tweet-div").append(thisUsersTweets[i]);   // append just this user's tweets
            }

        },"json");  // end of getJSON call
    }               // end main "for" loop
}                   // end of getTweets()

If you need to collect the entire list of tweets into an array for later use in the function, then you could do that too, but since you haven't requested that, my sample doesn't do that.

Now that you've posted your actual code and I see that you want to sort all the tweets after you've collected them all, here's a way of doing it in your actual code. In this version, we keep a count of how many tweet requests you've launched and when all responses have been received, we call insertTweets.

I've modified your actual code to that that here. There are only four changes:

  1. Add a responsesRemaining variable at the top level of the function
  2. Remove the timeout call
  3. Increment the responsesRemaining variable before each JSON call
  4. At the end of the success handler, decrement the responsesRemaining variable and, if it's down to zero (all responses received), call insertTweets().

This version of the code will have these advantages over your setTimeout version:

  1. It will work regardless of the timing of the tweet responses.
  2. It will insert the tweets as soon as they are all available without waiting some set amount of time so it will show the results faster.
  3. If any of the requests to get the tweets take longer to succeed, this method will still show their tweets (yours would not).

Here's the modified code:

function getTweets()
{
//retrieves a JSON file from twitter.com with the information specified in the URL//parameters. A description of parameters can be found at//https://dev.twitter.com/docs/api/1/get/statuses/user_timelinevartweetArray=newArray();//holds HTML-formatted tweets for everyonevarresponsesRemaining=0;

varusers=newArray();//holds all the user account infovaruser1="[twitter name]";
varuser2="[twitter name]";
varuser3="[twitter name]";
varuser4="[twitter name]";
varuser5="[twitter name]";
varuser6="[twitter name]";
varuser7="[twitter name]";
varuser8="[twitter name]";

users.push(user1,user2,user3,user4,user5,user6,user7,user8);

for(var n=0;n<users.length;n++)
{
    ++responsesRemaining;
    $.getJSON("http://api.twitter.com/1/statuses/user_timeline.json?screen_name="+users[n]+"&count=10&include_rts=true&callback=?",
    function(tweets){
        $.each(tweets, function(i,item){

            var tweetString;

            //the names of the various data can be found//in the twitter API reference pages, such as//https://dev.twitter.com/docs/api/1/get/statuses/user_timelinevartweetText='<div class="tweet-text" id="tweet-text'+i+'" >'+item.text+'</div>';
            varuserID= item.user.id_str;
            varuserName='<div class="tweet-user-name" id="tweet-user-name'+i+'" >'+item.user.name+'</div>';
            varuserPic='<img class="tweet-img" id="tweet-img'+i+'" src="'+item.user.profile_image_url +'" />';

            //formats created_at data from twitter into a nice date/timevartweetDate= item.created_at;
            varhourminute= tweetDate.substr(tweetDate.indexOf(":")-2,5);
            varhour= parseInt(hourminute.substr(0,2))+7;//the +7 is a time zone correctionif (hour > 12)
            {
                hour = hour-12;
            }
            elseif(hour == 0)
            {
                hour = 12;
            }
            varampm="AM";
            if(hour>=12)
            {
                ampm = "PM";
            }
            varminute= hourminute.substr(3,2);
            varday= tweetDate.substr(0,3)
            vardateNum= tweetDate.substr(8,2);
            varmonth= tweetDate.substr(4,3);
            switch(month)
            {
                case"Jan":
                month="01"break;
                case"Feb":
                month="02"break;
                case"Mar":
                month="03"break;
                case"Apr":
                month="04"break;
                case"May":
                month="05"break;
                case"Jun":
                month="06"break;
                case"Jul":
                month="07"break;
                case"Aug":
                month="08"break;
                case"Sep":
                month="09"break;
                case"Oct":
                month="10"break;
                case"Nov":
                month="11"break;
                case"Dec":
                month="12"break;
            }
            varyear= tweetDate.substr(-4,4);
            vardateFormatted='<div class="tweet-date" id="tweet-date'+i+'" >'+day+' '+month+'.'+dateNum+'.'+year+' • ' + hour +':'+ minute +' '+ ampm+'</div>';

            //reformats the date yet again so that tweets can be sorted chronologicallyvarsortableDate= month+dateNum+year;

            //combines all tweet information into one string of HTML
            tweetString = '<div class="tweet" id="tweet'+i+'">'+ dateFormatted+userName+tweetText + '</div>';

            vartempArray=newArray();
            tempArray=[sortableDate,tweetString];

            //pushes formatted tweet HTML code into an array
            tweetArray.push(tempArray);
        });
        --responsesRemaining;
        if (responsesRemaining <= 0) {
            insertTweets(tweetArray);
        }
    },"json");
}
}


function insertTweets(content)
{

//sort tweets in tweetArray by date instead of by author
content = content.sort();
content = content.reverse();

//change or remove the "Loading Tweets" messageif(content == "")
{
    $("#load-status").html("There was an error retreiving tweets.");
}
else
{
    $("#load-status").empty();
}

//loops through tweetArray and inserts HTML code into pagefor(var i=0;i<content.length;i++)
{
    $("#tweet-box").append(content[i][1]);
}

//create patterned background effect for tweets
$(".tweet:odd").css("background-color","#f0f0f0");
$(".tweet:even").css("background-color","#dddddd");
}

Solution 2:

Am I correct in guessing that because the .getJSON success function is a separate function, it does not have access to the local variable tweetArray from my getTweets() function?

Exactly. You can fix this by making tweetArray a global variable by placing it outside of the function:

var tweetArray = newArray();
functiongetTweets()
{
    ...
}

Post a Comment for "Ajax And Variable Scope"