2011-08-02 159 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

不是。具有全局變量與所有其他網站潛在的延遲瓶頸相比,不會以任何有意義的方式影響性能。 –