0

我想编写一个从 youtube 视频返回信息的 javascript 函数;更具体地说,我想在 json 对象中获取通过搜索获得的视频的 ID 和长度。因此,我查看了 youtube API,并提出了以下解决方案:

   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,你们中的许多人已经理解了,我得到一个空数组。反正有没有得到完整的对象?我应该使用延迟对象吗?感谢您的回答。

4

2 回答 2

4

是的,您应该使用延迟对象。

这里最简单的方法是创建一个数组,您可以在其中存储jqXHR内部$.getJSON()调用的结果。

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

有关使用此 YouTube API 的演示,请参阅http://jsfiddle.net/alnitak/8XQ4H/,以了解延迟对象如何让您将 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
});
于 2013-05-31T13:03:59.863 回答
0

正如您所发现的,您不能youtubeMap直接返回,因为它尚未在返回点填充。但是您可以返回一个完全填充的PromiseyoutubeMap,可以使用 eg.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并显示.idand.runtime数据。
  • 顺序查询比并行查询更可取,因为顺序查询对客户端和服务器都更友好,并且更有可能成功。
  • 另一种有效的方法是返回一组单独的承诺(每个视频一个)并用 来响应完成$.when.apply(..),但是提取所需的数据会更难。
于 2013-05-31T18:22:16.610 回答