jQuery Deferred 应该可以工作,但不能

Posted

技术标签:

【中文标题】jQuery Deferred 应该可以工作,但不能【英文标题】:jQuery Deferred should work but doesn't 【发布时间】:2013-03-29 15:26:17 【问题描述】:

我有一个 Spotify 应用程序,想等待一个循环完成。但它只在我的应用重新加载一次后才有效。

function matchRecommendations(result) 
var deferreds = new Array();
for ( var i = 0; i < result.length; i++) 
    // Async wait
    var dfd = $.Deferred();
    deferreds.push(dfd.promise());

    ...

    // Wait for search to finish
    search.tracks.snapshot(0, 1).done(
            function(snapshot) 
                // If match was found => create recommendation object
                var uri = snapshot._uris[0];
                var meta = snapshot._meta[0];
                if ($.type(meta) !== "undefined") 
                    if ($.type(meta.name) === "string") 
                        console.log(uri);
                        var rec = new Recommendation(uri, meta,
                                explanation, score);
                        RadioView.prototype.addRecommendation(rec);
                    
                

                // Async task finished
                dfd.resolve();
            );


return deferreds;
 

这里我调用了上面的方法,想等它完成。

 $.when.apply($, matchRecommendations(result)).done(
                            function() 
                                console.log("finished");
                                RadioView.prototype.render();
                            );

我看不出为什么这个灵魂不起作用的问题。问题是第一次加载时没有执行“完成”回调。如果我重新加载应用程序,它工作得很好 ...延迟的东西有什么问题吗?

【问题讨论】:

如果snapshot() 已经返回了一个promise,done() 的使用意味着,你为什么不只是返回那个promise 而不是创建一个新的? 确定过程中没有出现js错误或者ajax错误?因为你的 when 应用于这个 deferreds 数组似乎完全没问题。 【参考方案1】:

这确实是一个老生常谈的范围问题,matchRecommendations() 中只有一个范围,并且在其for 循环中设置的所有变量都有其最终值,而当该循环退出时没有其他值。

有很多方法可以解决这个问题,最简单的(因为您已经在使用 jQuery)是使用 jQuery.each() 循环遍历 result 数组。

代码如下所示:

function matchRecommendations(result) 
    var promises = [];
    $.each(result, function(i, rslt) 
        ...
        var p = search.tracks.snapshot(0, 1).done(function(snapshot) 
            var uri = snapshot._uris[0];
            var meta = snapshot._meta[0];
            if ($.type(meta) !== "undefined" && $.type(meta.name) === "string") 
                console.log(uri);
                RadioView.prototype.addRecommendation(new Recommendation(uri, meta, explanation, score));
            
        );
        promises.push(p);
    );
    return promises;
 

注意事项:

正如@adeneo 指出的那样,search.tracks.snapshot() 返回一个承诺(至少可以安全地假设它确实如此),因此无需创建二级 Deferred 以在级联中解决。 (Doh!)如果没有第二个 Deferred,范围问题实际上会消失,因此您可以根据需要返回到 for 循环。

【讨论】:

您的所有建议都可以正常工作;)...但就像我的第一种方法一样,不是第一次。所以我想延迟的东西毕竟不是问题。必须查看 spotify preview api 的那些很棒的文档...无论如何都重写了我的方法来使用每个 jquery,谢谢! Sven,我不能对 Spotify 发表评论,因为我不是 Spotify 人,但他们似乎对文档有着坚定的承诺。希望你会没事的。祝你好运。【参考方案2】:

您的代码似乎存在范围问题,这就是为什么可能只有最后一个 deferred 会得到解决的原因。我不知道您如何检查正在执行的 done 回调(未执行),但范围问题可能会导致您的问题(除非 result.length 恰好是 1)。

您需要为每个 deferred 创建一个新范围:

var deferreds = [];
for (var i = 0; i < result.length; i++) 
  (function(dfd) 
    deferreds.push(dfd.promise());
    search.tracks.snapshot(0, 1).done(function(snapshot) 
      ...
      dfd.resolve();
    );
  )($.Deferred());
;

如果result 恰好是一个数组,您可以创建更易读的代码:

var deferreds = $.map(result, function() 
  var dfd = $.Deferred();
  search.tracks.snapshot(0, 1).done(...);
  return dfd.promise();
);    

【讨论】:

以上是关于jQuery Deferred 应该可以工作,但不能的主要内容,如果未能解决你的问题,请参考以下文章

有人可以清楚地解释 jQuery.when() 和 deferred.then() 的工作原理吗?

jQuery Deferred/promise 没有按预期工作

jQuery异步框架探究2:jQuery.Deferred方法

通过 ES6 Promise 和 jQuery Deferred 的异同学习 Promise

jQuery异步框架探究2:jQuery.Deferred方法

jQuery异步框架探究2:jQuery.Deferred方法