如何在$ .each()循环中保持$ .ajax()异步,但在每次迭代时对结果做出反应

Sou*_*ess 5 javascript youtube ajax jquery bookmarklet

我有些笨拙的体系结构遇到了麻烦,因为我很笨拙。我试图遍历发布到Reddit的YouTube视频,提取URL并将其处理为.m3u播放列表。

Subreddit到YouTube源书签的完整代码-使用foo_youtube播放Foobar中subreddit的YouTube音乐

在某个时候,我想到了可以检查每个URL来查看视频是否已消失的想法,并提供删除视频的替代方法。

因此,我向YouTube API发出了AJAX请求,如果出现错误,我应该对此做出反应并更改该项目的URL。

但是问题在于,只有在AJAX不异步的情况下它才有效-这需要很多秒钟,在此期间页面会被卡住。

我想让AJAX异步,但是我不知道如何构造我的代码。

这是现在的伪代码

var listing = // data from reddit API
$.each(listing, function(key, value) {
    var url = // post URL from reddit posts listing 
    // ( "http://youtu.be/XmQR4_hDtpY&hd=1" )
    var aRegex = // to parse YouTube URLs 
    // ( (?:youtube(?:-nocookie)?.com/....bla...bla )
    var videoID = // YouTube video ID, extracted with regex 
    // ( "XmQR4_hDtpY" )
    var itemArtist = // parsed from reddit posts listing 
    // ( "Awesome Artist" )
    var itemTitle = // parsed from reddit posts listing 
    // ( "Cool Song (Original Mix)" )
    var itemURL = // url, further processed 
    // ( "3dydfy://www.youtube.com/watch?v=XmQR4_hDtpY&hd=1" )

    $.ajax({
            type: "HEAD",
            url: "https://gdata.youtube.com/feeds/api/videos/" + videoID,
            error: function() { 
                // If it's no longer available 
                // (removed, deleted account, made private)
                deadVid++; // chalk up another dead one, for showing progress
                itemURL = // dead videos should get a different URL 
           // ( "3dydfy-search://q=Awesome%20Artist%20Cool%20Song....." )
            }
        });

    // further process itemURL!
    // at this point I want itemURL changed by the .ajax()'s error callback
    // but I'm trying to keep the requests async 
    // to not jam the page while a hundred HTTP requests happen!
    if (condition){
        itemURL += // append various strings
    }
    // Write the item to the .m3u8 playlist
    playlist += itemURL + '\n';
}// end .each()
Run Code Online (Sandbox Code Playgroud)

Gon*_*ing 5

基本上你想知道

  • 错误是什么以及
  • 当每个ajax请求完成时

如果您将错误推送到列表中,结果将在最后准备好(当然不能保证顺序)。

对于第二部分,如果您保留从每个 $.ajax 返回的 ajax 承诺的数组,您可以使用$.when并等待它们全部使用always().

作为一个基本示例(其他细节已删除):

var listing = {}; // data from reddit API
var promises = [];
var results = [];
$.each(listing, function(key, value) {
    // [snip]
    promises.push($.ajax({
            type: "HEAD",
            url: "https://gdata.youtube.com/feeds/api/videos/" + videoID,
            error: function() { 
                //[snip]
                results.push({
                   itemArtist: itemArtist,
                   videoID: videoID,
                   url: itemURL});
            }
        });
    );
}
// Wait for all promises to complete (pass or fail) 
$.when.apply($, promises).always(function(){
    // process list of failed URLs
});
Run Code Online (Sandbox Code Playgroud)

为任何错别字道歉。这直接编码到答案中,但您明白了。

我注意到您提到了 100 个请求,但浏览器一次只允许少数几个通过,因此不需要额外的处理。

如果always不起作用,您可以添加自己的延迟对象,这些对象在success 上解析fail

var listing = {}; // data from reddit API
var promises = [];
var results = [];
$.each(listing, function(key, value) {
    var deferred = $.Deferred();
    promises.push(deferred.promise());
    // [snip]
    $.ajax({
            type: "HEAD",
            url: "https://gdata.youtube.com/feeds/api/videos/" + videoID,
            complete: function(){
                // resolve on success or fail
                deferred.resolve();
            },
            error: function() { 
                //[snip]
                results.push({
                   itemArtist: itemArtist,
                   videoID: videoID,
                   url: itemURL});
            }
        });
    );
}
// Wait for all promises to complete (pass or fail) 
$.when.apply($, promises).always(function(){
    // process list of failed URLs
});
Run Code Online (Sandbox Code Playgroud)

2015 年 12 月更新

这是将并行 promise 链接在一起的另一种很酷的方法,而不使用数组(只要您不需要传递给回调的数据值):

简化代码:

   var promise;   // Undefined is also a resolved promise when passed to $.when()
   $.each(listing, function(key, value) {
       // Each new Ajax promise is chained, in parallel, with the previous promise
       promise = $.when(promise, $.ajax({...}));
   });
   // When they are all finished, fire a final callback
   $.when(promise).always(function(){
       // All done!
   });
Run Code Online (Sandbox Code Playgroud)

这受到了一些批评,主要来自认为它“不干净”的人,但对于并行承诺代码的简化,权衡是最小的。

当我看到有人promise = promise.then(newpromise)习惯于按顺序链接事件时,我发现这是可能的。经过一些实验后,我发现我可以使用并行执行相同的操作promise = $.when(promise, newpromise)