2013-05-31 109 views
0

我想編寫一個javascript函數,它返回來自YouTube視頻的信息;更具體一點,我想通過一個json對象獲得搜索得到的視頻的ID和長度。所以我看了看YouTube的API,我想出了這個解決方案:從嵌套了ajax調用的函數返回對象

function getYoutubeDurationMap(query){ 
     var youtubeSearchReq = "https://gdata.youtube.com/feeds/api/videos?q="+ query + 
       "&max-results=20&duration=long&category=film&alt=json&v=2"; 
     var youtubeMap = []; 
     $.getJSON(youtubeSearchReq, function(youtubeResult){ 
      var youtubeVideoDetailReq = "https://gdata.youtube.com/feeds/api/videos/"; 
      for(var i =0;i<youtubeResult.feed.entry.length;i++){ 
       var youtubeVideoId = youtubeResult.feed.entry[i].id.$t.substring(27); 
       $.getJSON(youtubeVideoDetailReq + youtubeVideoId + "?alt=json&v=2",function(videoDetails){ 
        youtubeMap.push({id: videoDetails.entry.id.$t.substring(27),runtime: videoDetails.entry.media$group.media$content[0].duration}); 

       }); 
      } 
     }); 
     return youtubeMap;  

    } 

的邏輯是好的,但正如你們許多人已經因爲AJAX的理解,當我把這個功能我得到一個空數組。無論如何要獲得完整的對象嗎?我應該使用Deferred對象嗎?感謝您的回答。

+1

不,您應該提出同步請求。 但是不要這樣做:學習使用Javascript的異步邏輯,並且你也可以很好地處理其他許多事情。 – MaxArt

+0

執行回調函數... – writeToBhuwan

+0

是的,你應該使用延遲對象。 – Alnitak

回答

4

是的,你應該使用延遲對象。

這裏最簡單的方法是創建一個數組,您可以在其中存儲調用內部$.getJSON()jqXHR結果。

var def = []; 
for (var i = 0; ...) { 
    def[i] = $.getJSON(...).done(function(videoDetails) { 
     ... // extract and store in youtubeMap 
    }); 
} 

,然後在整個函數結束,使用$.when創建一個新的承諾,將只解決了當所有內部通話已經完成:

return $.when.apply($, def).then(function() { 
    return youtubeMap; 
}); 

然後使用.done從你的函數處理結果:

getYoutubeDurationMap(query).done(function(map) { 
    // map contains your results 
}); 

請參閱http://jsfiddle.net/alnitak/8XQ4H/瞭解如何使用此YouTube API演示延遲對象如何讓您將AJAX調用與「持續搜索」的後續數據處理完全分開。

這段代碼有點長,但也在這裏轉載。但是,儘管代碼比您預期的要長,但請注意,此處的通用功能現在可以重複使用,您可能希望對YouTube API調用任意調用。

// generic search - some of the fields could be parameterised 
function youtubeSearch(query) { 
    var url = 'https://gdata.youtube.com/feeds/api/videos'; 
    return $.getJSON(url, { 
     q: query, 
     'max-results': 20, 
     duration: 'long', category: 'film', // parameters? 
     alt: 'json', v: 2 
    }); 
} 

// get details for one YouTube vid 
function youtubeDetails(id) { 
    var url = 'https://gdata.youtube.com/feeds/api/videos/' + id; 
    return $.getJSON(url, { 
     alt: 'json', v: 2 
    }); 
} 

// get the details for *all* the vids returned by a search 
function youtubeResultDetails(result) { 
    var details = []; 

    var def = result.feed.entry.map(function(entry, i) { 
     var id = entry.id.$t.substring(27); 
     return youtubeDetails(id).done(function(data) { 
      details[i] = data; 
     }); 
    }); 

    return $.when.apply($, def).then(function() { 
     return details; 
    }); 
} 

// use deferred composition to do a search and then get all details 
function youtubeSearchDetails(query) { 
    return youtubeSearch(query).then(youtubeResultDetails); 
} 

// this code (and _only_ this code) specific to your requirement to 
// return an array of {id, duration} 
function youtubeDetailsToDurationMap(details) { 
    return details.map(function(detail) { 
     return { 
      id: detail.entry.id.$t.substring(27), 
      duration: detail.entry.media$group.media$content[0].duration 
     } 
    }); 
} 

// and calling it all together 
youtubeSearchDetails("after earth").then(youtubeDetailsToDurationMap).done(function(map) { 
    // use map[i].id and .duration 
}); 
+0

非常感謝! – user1012480

0

當你發現,你不能直接,因爲它是尚未填充在返回點返回youtubeMap。但是您可以返回完全填充的youtubeMap的承諾,該承諾可以使用例如.done(), .fail().then()加以處理。

function getYoutubeDurationMap(query) { 
    var youtubeSearchReq = "https://gdata.youtube.com/feeds/api/videos?q=" + query + "&max-results=20&duration=long&category=film&alt=json&v=2"; 
    var youtubeVideoDetailReq = "https://gdata.youtube.com/feeds/api/videos/"; 
    var youtubeMap = []; 
    var dfrd = $.Deferred(); 
    var p = $.getJSON(youtubeSearchReq).done(function(youtubeResult) { 
     $.each(youtubeResult.feed.entry, function(i, entry) { 
      var youtubeVideoId = entry.id.$t.substring(27); 
      //Build a .then() chain to perform sequential queries 
      p = p.then(function() { 
       return $.getJSON(youtubeVideoDetailReq + youtubeVideoId + "?alt=json&v=2").done(function(videoDetails) { 
        youtubeMap.push({ 
         id: videoDetails.entry.id.$t.substring(27), 
         runtime: videoDetails.entry.media$group.media$content[0].duration 
        }); 
       }); 
      }); 
     }); 
     //Add a terminal .then() to resolve dfrd when all video queries are complete. 
     p.then(function() { 
      dfrd.resolve(query, youtubeMap); 
     }); 
    }); 
    return dfrd.promise(); 
} 

並調用getYoutubeDurationMap()將是以下形式:

getYoutubeDurationMap("....").done(function(query, map) { 
    alert("Query: " + query + "\nYouTube videos found: " + map.length); 
}); 

注:

  • 在實踐中,你可能會遍歷map並顯示.id.runtime數據。
  • 順序查詢優於並行查詢,因爲順序對客戶端和服務器都很友善,並且更有可能成功。
  • 另一個有效的方法是返回一組單獨的promise(每個視頻一個),並使用$.when.apply(..)來完成響應,但是所需的數據將更難提取。
+0

不錯,但根據我的經驗,在系列中運行查詢會使批處理速度慢於瀏覽器,並且如果並行運行,瀏覽器_should_將併發AJAX查詢數量限制爲4個左右。 – Alnitak

+0

是的,它會運行得更慢,但更有保證完成。我沒有對瀏覽器在這方面的行爲進行全面的審查,但不會在他們所有人身上施加保護。請記住,現在還有一些流行的「也是rans」,特別是在Linux-land(包括Raspberry PI)中 - Midori,Dillo,Chromium,NetSurf,IceWeasel ...。他們如何表現?我沒有一點線索。 –