2011-08-02 122 views
0

我正在写一个脚本,它读取多个Twitter供稿并将它们写入一个div。当我向一个特定的Feed提交1个AJAX请求时,该代码正常工作,但是当我尝试循环访问一个Feed列表时,我什么也得不到。我敢肯定,变量范围是责备在这里,但我不知道如何解决它在Javascript中。ajax和变量范围

这里是1 - 饲料版本(的方式使用jQuery)的作品,稍作简化:

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(); 

    $.each(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"); 
} 

因此,获取JSON数据,通过其内容的一些数据转换成HTML和填充循环,HTML到数组中,然后遍历该数组以将其内容填充到我的DOM中。

下面是我使用的尝试读取多个源(和失败)的代码:

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]); 
} 
} 

代码#2最大的区别是,我感动tweetArray从侧面.getJSON成功功能外的。看来正在发生的事情是,当我在末尾的.append()部分循环访问tweetArray时,tweetArray中没有任何内容。

我正确的猜测,因为.getJSON成功函数是一个单独的函数,它没有从我的getTweets()函数访问局部变量tweetArray吗?

如果是这样,我怎么能解决这个问题,同时仍然能够循环通过我的用户列表,因为我正在尝试#2?

我很抱歉,如果我在这里停留的代码中有任何拼写错误,但小的语法错误不是这里的问题,因为我的网站#1工作得很好。


更新:我意识到,如果我插入警报(tweetArray)到我的代码之前,我尝试将tweetArray数据追加到我的DOM,它的工作原理,甚至无需做出tweetArray一个全局变量。现在我对于发生的事情更加无知。


更新:下面是实际的代码复制/粘贴我使用:

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"); 
} 
+0

OK,另一个奇怪的问题。我认为我已经通过使tweetArray成为一个全局变量解决了这个问题。对于测试,我的.append()代码之前有alert(tweetArray)。当我删除警报时,整个功能停止工作!我不知道发生了什么事。 – penco

+0

请阅读OP顶部的UPDATE。 – penco

+0

alert()解决问题的原因是alert()导致代码暂停足够长的时间以使ajax成功函数运行。它与我最初想象的变量范围无关。 – penco

回答

1

我在以前的答案相反的方向前进。你不希望tweetArray是全局的。

首先,如果你什么都没有,那么你的代码中可能有语法错误,你需要找到,因为多用户代码应该给你太多(重复推文)而不是无关紧要。由于这是伪代码,而不是真正的代码,所以我们无法帮助确定语法错误的位置。由于getJSON成功函数处理程序中可能存在语法错误,因此您可能不会直接在调试程序中看到语法错误。要看到它在哪里,你需要把一个断点回调的开始,并通过它一步,看看它去坚果或者把一个try/catch回调和输出里面的一些调试信息,如果它抛出一个异常。

除了潜在的语法错误之外,我在第二块代码中看到的问题是,您在所有成功处理程序中共享tweetArray,但是在每个成功处理程序中,您遍历整个tweetArray并追加结果。这意味着第一个JSON调用将起作用,因为它会将最初的推文收集到数组中,然后将它们全部追加。第二个JSON调用将添加到相同的数组,然后遍历整个数组并将它们全部追加。这将再次插入来自第一次通话的推文 - 不是你想要的。第三次电话会再次复制之前发出的所有推文。问题的部分解决方案是将tweetArray放入成功函数中,以便您只收集并插入来自特定JSON调用的推文。

要获得每个鸣叫附加恰好一次,我想你需要改变这样的:

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() 

如果您需要收集鸣叫的整个列表到在功能以后使用数组,然后你也可以这样做,但由于你没有要求,我的样本不这样做。

现在,您已经发布您的实际代码,我看到你想要所有的鸣叫排序您收集了之后他们,这里是在您的实际代码做这件事的方式。在这个版本中,我们会记录您发起的推文请求数量以及收到所有响应的时间,我们称之为insertTweets。

我已经修改了你的实际代码到这里。只有四个转变:

  1. 在功能
  2. 的顶层添加responsesRemaining变量清除超时通话
  3. 递增每个JSON调用
  4. 在成功结束前的responsesRemaining变量处理程序,递减responsesRemaining变量,如果它下降到零(所有收到的回应),调用insertTweets()

这个版本的代码都将具有这些优点在你的setTimeout版本:

  1. 将工作无关的鸣叫响应的时机。
  2. 将尽快为他们都可用插入鸣叫,而无需等待一定的时间设定量,它会显示结果快。
  3. 如果任何请求,以获得鸣叫需要更长的时间才能成功,这种方法仍然会显示他们的鸣叫(你不会)。

下面是修改后的代码:

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"); 
} 
+0

在我的实际代码中,将推文附加到DOM的FOR循环在读取推文的FOR循环之外。我将它固定在我原来的帖子中。我感谢你的代码;但是,我确实需要存储整个阵列供以后使用。在我将所有推文收集到我的tweetArray后,我按日期对它们进行排序,以便在我的网站上,推文按日期排序,而不是由用户排序。如果你有一个想法,我可以做到这一点,而不使用tweetArray的全局变量,我全是耳朵。另外,对于我在OP的评论中提出的问题的第二部分,我仍然无能为力。 – penco

+0

那就解释了这个问题。 getJSON调用尚未完成。您的getJSON调用是异步的,只有通话才会启动该呼叫。他们还没有完成,直到成功处理程序被调用,所以你试图将它们添加到页面之前,甚至已经填充数组。我会建议按照我在这里完成的方式进行。然后,当成功处理程序拥有数据时,每个电话的推文都会被添加。 – jfriend00

+0

成功!实际上我只是在执行.getJSON的循环之后坚持setTimeout,并且在2秒之后它将tweetArray传递给另一个插入的函数。谢谢你的帮助。我确信这个问题与范围或关闭有关,所以我没有办法。 – penco

1

我是正确的猜测,因为.getJSON成功函数是独立的功能,但它确实无法从我的getTweets()函数访问本地变量tweetArray?

没错。您可以通过将其放置在函数外通过使tweetArray一个全局变量解决这个问题:

var tweetArray = new Array(); 
function getTweets() 
{ 
    ... 
} 
+0

哇,它的工作!我不敢相信这很容易。 – penco

+0

但是还有一个问题:我基本上为一个多页面网站使用了一个巨大的.js文件,并且这些网页中只有1个需要与这个twitter feed有任何关系。是否会出现任何与性能相关的问题或类似于我的所有页面都有这个全局变量的东西?我只习惯于面向对象的语言,其中有更多的范围选项比“无处不在”或“绝对只有一个地方”。 – penco

+0

不是。具有全局变量与所有其他网站潜在的延迟瓶颈相比,不会以任何有意义的方式影响性能。 –