【问题标题】:use jquery deferreds for variable number of ajax requests对可变数量的 ajax 请求使用 jquery deferreds
【发布时间】:2013-02-20 20:48:34
【问题描述】:

当我有可变数量的 ajax 请求时,如何使用延迟调用它们?

我的猜测:

//qty_of_gets = 3;

function getHTML(productID, qty_of_gets){

    var dfd = $.Deferred(),
            i = 0,
            c = 0;

    //this is where there could be some magic to 
    //do multiple ajax posts
    //obviously I'm out of my depth here...
    while (i <= qty_of_gets){

        dfd.pipe(function(){
            $.get("queries/html/" + product_id + i + ".php");
        });                       
    i++
    }
    dfd.done(function(){

        while (c <= qty_of_gets){
           $('myDiv').append(c);
           c++;
        }

    });
}

【问题讨论】:

  • 如果你只是将 ajax 请求自动返回的延迟对象粘贴到一个数组中,然后用一点 apply() 将数组放入 $.when 中,你就会到达那里!跨度>

标签: javascript jquery jquery-deferred


【解决方案1】:

如果你想按顺序执行 Ajax 调用,你必须从回调中返回 Promise,并在最后一个 Promise 对象上附加一个新的回调:

var dfd = $.Deferred(),
   promise = dfd.promise(),
   i = 0,
   c = 0;

while (i <= qty_of_gets) {
    // needs an IIFE
    (function(i)
        promise = promise.then(function(){
            return $.get("queries/html/" + product_id + i + ".php");
        });
    }(i++));                       

}

promise.done(function(){

    while (c <= qty_of_gets){
       $('myDiv').append(c);
       c++;
    }

});

// resolve deferred
dfd.resolve();

从 jQuery 1.8 开始,您应该使用 .then 而不是 .pipe

另一个问题是(至少在您的示例中)在执行回调时,i 不会具有您期望的值。您可以使用立即调用的函数表达式来捕获i 的当前值。请参阅JavaScript closure inside loops – simple practical example 了解更多信息。


没有获得结果的干净解决方案。我认为您能做的最好的事情就是将结果添加到一个数组并在.done 回调中访问该数组。即:

var results = [];

while (i <= qty_of_gets) {
    // needs an IIFE
    (function(i)
        promise = promise.then(function(){
            return $.get("queries/html/" + product_id + i + ".php")
                     .then(function(result) {
                       results[i] = result;
                     });
        });
    }(i++));                       

}

promise.done(function(){
    // do something with `results`
});

【讨论】:

  • 谢谢。我在此页面中读到问题是变量 i,在您的每个匿名函数中,都绑定到函数外部的同一个变量。 以及许多其他类似的解释。但是我不明白i是否绑定到匿名函数的内部,为什么每次经过循环时不重新传入。当然,它存在于外部,但它的值不是不断增加并传入。它是说第一次创建匿名函数时,该值以某种方式永久设置,因此 javascript 将始终读取console.log(1)跨度>
  • 几个问题:你说“你必须从回调中返回承诺”。这样就完成了promise的回调函数。所以你正在做的是像promise.then($.get("queries/1.php").then.($.get("queries/2.php")... 这样的链接?为什么将resolve() 放在done() 之后? jquery API 说“当 Deferred 被解析时,任何由 deferred.then 或 deferred.done 添加的 doneCallbacks 都会被调用。”再次感谢所有帮助!
  • 在循环的每次迭代中,都会创建一个新的匿名函数。您永远不会两次调用相同的函数。但是,您可以创建一个函数 function foo(promise, i) { return promise.then(function(){...}); } 并调用这个函数而不是匿名函数:promise = foo(promise, i)。一样的。关键是调用一个函数来创建一个新的范围。关于第二个问题,和拨打promise.then(function() { return $.get(...); }).then(...).then(...);一样。 .then 总是返回一个新的承诺。
  • 如果我不调用.resolve,则不会调用任何回调。我们希望第一个 Promise 对象调用其.then 回调,以便它开始发出这些 Ajax 请求。在延迟对象解决之前,它不会这样做。请注意,.done 回调被添加到链中的 last 承诺对象,而不是延迟对象本身。我也可以在创建延迟对象后立即调用.resolve,这不会产生影响。
  • 我的意思是物理上在页面上,它在done() 函数下方调用。如果在解决承诺之前无法调用done,为什么它在done之下?
【解决方案2】:

关闭,你需要从.pipe回调中返回一个promise对象。
请参阅 felix 的回答,下一个示例除了缺少返回之外还有其他问题。

dfd.pipe(function(){
    return $.get("queries/html/" + product_id + i + ".php");
});  

另外,我认为它实际上还没有写在任何地方,但是 .pipe 在最近版本的核心中是这样实现的:

promise.pipe = promise.then

因此,您可以应将dfd.pipe 替换为dfd.then
参考:http://api.jquery.com/deferred.pipe/

提到的 adeneo 的替代方法是使用 $.when

function getHTML(productID, qty_of_gets) {

    var dfdArr = [];

    while (i <= qty_of_gets) {
        dfdArr.push($.get("queries/html/" + product_id + i + ".php"));
        i++
    }
    $.when.apply(null, dfdArr).done(function () {

        for (response in arguments) {
            $('#myDiv').append(response[0]);
        }

    });
}

【讨论】:

  • 仅供参考,这是一个相对较新的发展。根据the specthen 应该返回一个promise,但是jQuery 中的原始promise 实现并没有这样做;他们反而创建了一个不同的pipe 方法。为了更符合规范(他们仍然不完全符合),他们改变了then 的行为,所以他们只是将pipe 别名为then。我相信 pipe 自 1.8 以来已被弃用,应该避免使用。
  • @JosephSilber 是的,由于 API 站点在 1.9 发布之前根本没有更新到 1.8,因此折旧通知从未到达 API 站点。它现在当然被记录为折旧。
  • 谢谢。你能解释一下$.when.apply(null, dfdArr).done 是如何工作的吗?究竟做了什么?另外,推入这样的数组是否会使各种gets 保持在顺序链中?附加product_id+1,然后附加product_id+2,等等?或者它们是按最快返回的顺序推入数组中的吗? while 循环将继续执行而不等待异步请求的结果,对吧?再次感谢
  • .when 方法接受 n 个参数,使用 .apply 将延迟对象数组作为参数应用到 .when 方法,从而更容易对其应用动态数量的参数。此方法和您的方法都不能保证请求将以什么顺序完成,但是它确实保证每个请求都将使用正确的 i 值触发,并且当它们都成功时,最后的 .done 将被触发都成功了。
  • 两者的区别在于 .then 方式只会给你最后一个发送的结果,而 .when 方式会让你访问所有请求的所有返回数据,按顺序查看返回的参数。例如,您可以使用for (args in arguments) { console.log(args[0]) } 以从0n 的顺序从每个请求中获取返回的文本。
猜你喜欢
  • 2021-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-11
  • 1970-01-01
  • 1970-01-01
  • 2016-12-28
  • 1970-01-01
相关资源
最近更新 更多