2013-05-31 75 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 ...。他们如何表现?我没有一点线索。 –