开发者

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 through a list of feeds, I get nothing. I'm pretty sure that variable scope is to blame here, but I don't know how to resolve it in Javascript.

Here's the 1-feed version that works, slightly simplified (using jQuery by the way):

function getTweets()
{
$.getJSON("http://api.twitter.com/1/statuses/user_timeline.json?screen_name=[my username]&count=10&include_rts=true&callback=?",
function(tweets){

    var tweetArray = new Array();

    $.eac开发者_开发问答h(tweets, function(i,item){
        var tweet = item.text;
        var htmlString = ...;
        tweetArray.push(htmlString);
    });

    for(var i=0;i<tweetArray.length;i++)
    {
        $("#tweet-div").append(tweetArray[i]);
    }

},"json");
}

So, fetch the JSON data, loop through its contents converting some data into HTML and stuffing that HTML into an array, and then looping through that array to stuff its contents into my DOM.

Here's the code I'm using to try to read multiple feeds (and failing):

function getTweets()
{
var tweetArray = new Array();
var users = new Array();
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){
        $.each(tweets, function(i,item){
            var tweet = item.text;
            var htmlString = ...;
            tweetArray.push(htmlString);
        });



},"json");

}//end main FOR loop

 for(var i=0;i<tweetArray.length;i++)
 {
    $("#tweet-div").append(tweetArray[i]);
 }
}

The big difference in code #2 is that I moved tweetArray from side the .getJSON success function to outside of it. What appears to be happening is that when I am looping through the tweetArray in my .append() portion at the end, nothing is in tweetArray.

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?

If so, how can I fix this while still being able to loop through my user list as I am trying to do in #2?

I apologize if there are any typos in the code I stuck in here, but minor syntax errors are not the problem here because on my website #1 is working perfectly fine.


UPDATE: I realized that if I insert alert(tweetArray) into my code before I try to append the tweetArray data to my DOM, it works without even needing to make tweetArray a global variable. Now I am even more clueless as to what is going on.


UPDATE: Here is a copy/paste of actual code I am using:

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_timeline
var tweetArray = new Array();//holds HTML-formatted tweets for everyone

var users = new Array();//holds all the user account info
var user1 = "[twitter name]";
var user2 = "[twitter name]";
var user3 = "[twitter name]";
var user4 = "[twitter name]";
var user5 = "[twitter name]";
var user6 = "[twitter name]";
var user7 = "[twitter name]";
var user8 = "[twitter name]";

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

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){
        $.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_timeline
            var tweetText = '<div class="tweet-text" id="tweet-text'+i+'" >'+item.text+'</div>';
            var userID = item.user.id_str;
            var userName = '<div class="tweet-user-name" id="tweet-user-name'+i+'" >'+item.user.name+'</div>';
            var userPic = '<img class="tweet-img" id="tweet-img'+i+'" src="'+item.user.profile_image_url +'" />';

            //formats created_at data from twitter into a nice date/time
            var tweetDate = item.created_at;
            var hourminute = tweetDate.substr(tweetDate.indexOf(":")-2,5);
            var hour = parseInt(hourminute.substr(0,2))+7;//the +7 is a time zone correction
            if (hour > 12)
            {
                hour = hour-12;
            }
            else if(hour == 0)
            {
                hour = 12;
            }
            var ampm = "AM";
            if(hour>=12)
            {
                ampm = "PM";
            }
            var minute = hourminute.substr(3,2);
            var day = tweetDate.substr(0,3)
            var dateNum = tweetDate.substr(8,2);
            var month = 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;
            }
            var year = tweetDate.substr(-4,4);
            var dateFormatted = '<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 chronologically
            var sortableDate = month+dateNum+year;

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

            var tempArray = new Array();
            tempArray=[sortableDate,tweetString];

            //pushes formatted tweet HTML code into an array
            tweetArray.push(tempArray);
        });
    },"json");
}

//at this point in the code, the user's browser is still waiting to receive
//the JSON files from twitter, and tweetArray has no data in it. The next line
//causes the code to break for a few seconds while the data is retrieved from
//twitter before trying to insert anything into the DOM

var waitForJSON = setTimeout(function(){insertTweets(tweetArray)},2000);

}


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" message
if(content == "")
{
    $("#load-status").html("There was an error retreiving tweets.");
}
else
{
    $("#load-status").empty();
}

//loops through tweetArray and inserts HTML code into page
for(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");
}


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:

function getTweets()
{
    var users = new Array();
    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_timeline
var tweetArray = new Array();//holds HTML-formatted tweets for everyone
var responsesRemaining = 0;

var users = new Array();//holds all the user account info
var user1 = "[twitter name]";
var user2 = "[twitter name]";
var user3 = "[twitter name]";
var user4 = "[twitter name]";
var user5 = "[twitter name]";
var user6 = "[twitter name]";
var user7 = "[twitter name]";
var user8 = "[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_timeline
            var tweetText = '<div class="tweet-text" id="tweet-text'+i+'" >'+item.text+'</div>';
            var userID = item.user.id_str;
            var userName = '<div class="tweet-user-name" id="tweet-user-name'+i+'" >'+item.user.name+'</div>';
            var userPic = '<img class="tweet-img" id="tweet-img'+i+'" src="'+item.user.profile_image_url +'" />';

            //formats created_at data from twitter into a nice date/time
            var tweetDate = item.created_at;
            var hourminute = tweetDate.substr(tweetDate.indexOf(":")-2,5);
            var hour = parseInt(hourminute.substr(0,2))+7;//the +7 is a time zone correction
            if (hour > 12)
            {
                hour = hour-12;
            }
            else if(hour == 0)
            {
                hour = 12;
            }
            var ampm = "AM";
            if(hour>=12)
            {
                ampm = "PM";
            }
            var minute = hourminute.substr(3,2);
            var day = tweetDate.substr(0,3)
            var dateNum = tweetDate.substr(8,2);
            var month = 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;
            }
            var year = tweetDate.substr(-4,4);
            var dateFormatted = '<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 chronologically
            var sortableDate = month+dateNum+year;

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

            var tempArray = new Array();
            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" message
if(content == "")
{
    $("#load-status").html("There was an error retreiving tweets.");
}
else
{
    $("#load-status").empty();
}

//loops through tweetArray and inserts HTML code into page
for(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");
}


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 = new Array();
function getTweets()
{
    ...
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜