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

时间:2013-04-07 15:18:25

标签: jquery spotify deferred

我有一个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();
                            });

我看不出问题为什么这不起作用。问题是第一次加载时没有执行“完成”回调。如果我重新加载应用程序它工作得很好 ......延期材料出了什么问题?

2 个答案:

答案 0 :(得分:0)

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

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

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

答案 1 :(得分:0)

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

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

代码如下所示:

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循环。