33

我需要在不锁定浏览器的情况下发出一系列 N ajax 请求,并希望使用 jquery deferred 对象来完成此操作。

这是一个包含三个请求的简化示例,但我的程序可能需要排队超过 100 个(注意这不是确切的用例,实际代码确实需要确保步骤(N-1)成功,然后再执行下一个步):

$(document).ready(function(){

    var deferred = $.Deferred();

    var countries = ["US", "CA", "MX"];

    $.each(countries, function(index, country){

        deferred.pipe(getData(country));

    });

 });

function getData(country){

    var data = {
        "country": country  
    };


    console.log("Making request for [" + country + "]");

    return $.ajax({
        type: "POST",
        url: "ajax.jsp",
        data: data,
        dataType: "JSON",
        success: function(){
            console.log("Successful request for [" + country + "]");
        }
    });

}

这是写入控制台的内容(所有请求都是并行发出的,响应时间与预期的每个国家/地区的数据大小成正比:

Making request for [US]
Making request for [CA]
Making request for [MX]
Successful request for [MX]
Successful request for [CA]
Successful request for [US]

如何让延迟对象为我排队?我尝试将 done 更改为 pipe 但得到相同的结果。

这是期望的结果:

Making request for [US]
Successful request for [US]
Making request for [CA]
Successful request for [CA]
Making request for [MX]
Successful request for [MX]

编辑:

我很欣赏使用数组来存储请求参数的建议,但是 jquery deferred 对象能够对请求进行排队,我真的很想学习如何充分利用这个功能。

这实际上是我正在尝试做的事情:

when(request[0]).pipe(request[1]).pipe(request[2])... pipe(request[N]);

但是,我想一次将请求分配到管道中,以便有效地使用每个遍历:

deferred.pipe(request[0]);
deferred.pipe(request[1]);
deferred.pipe(request[2]);
4

6 回答 6

31

使用自定义对象

function DeferredAjax(opts) {
    this.options=opts;
    this.deferred=$.Deferred();
    this.country=opts.country;
}
DeferredAjax.prototype.invoke=function() {
    var self=this, data={country:self.country};
    console.log("Making request for [" + self.country + "]");

    return $.ajax({
        type: "GET",
        url: "wait.php",
        data: data,
        dataType: "JSON",
        success: function(){
            console.log("Successful request for [" + self.country + "]");
            self.deferred.resolve();
        }
    });
};
DeferredAjax.prototype.promise=function() {
    return this.deferred.promise();
};


var countries = ["US", "CA", "MX"], startingpoint = $.Deferred();
startingpoint.resolve();

$.each(countries, function(ix, country) {
    var da = new DeferredAjax({
        country: country
    });
    $.when(startingpoint ).then(function() {
        da.invoke();
    });
    startingpoint= da;
});

小提琴http://jsfiddle.net/7kuX9/1/

为了更清楚一点,最后几行可以写成

c1=new DeferredAjax( {country:"US"} );
c2=new DeferredAjax( {country:"CA"} );
c3=new DeferredAjax( {country:"MX"} );

$.when( c1 ).then( function() {c2.invoke();} );
$.when( c2 ).then( function() {c3.invoke();} );

带管道

function fireRequest(country) {
        return $.ajax({
            type: "GET",
            url: "wait.php",
            data: {country:country},
            dataType: "JSON",
            success: function(){
                console.log("Successful request for [" + country + "]");
            }
        });
}

var countries=["US","CA","MX"], startingpoint=$.Deferred();
startingpoint.resolve();

$.each(countries,function(ix,country) {
    startingpoint=startingpoint.pipe( function() {
        console.log("Making request for [" + country + "]");
        return fireRequest(country);
    });
});

http://jsfiddle.net/k8aUj/1/

编辑:在结果窗口http://jsfiddle.net/k8aUj/3/中输出日志的小提琴

每个管道调用都会返回一个新的 Promise,该 Promise 又用于下一个管道。请注意,我只提供了sccess功能,对于失败应该提供类似的功能。

在每个解决方案中,通过将 Ajax 调用包装在一个函数中,并为列表中的每个项目创建一个新的 Promise 来构建链,从而延迟 Ajax 调用直到需要。

我相信自定义对象提供了一种更简单的方式来操作链,但管道可能更适合您的口味。

注意:从 jQuery 1.8 开始,deferred.pipe()已弃用,deferred.then取而代之。

于 2011-12-23T09:55:23.017 回答
5

注意:从 jquery 1.8 开始,您可以.then使用.pipe. 该.then函数现在返回一个新的 Promise 并被.pipe弃用,因为它不再需要。有关 Promise的更多信息,请参阅Promise规范,以及q.js以获得更干净的 javascript Promise 库,无需 jquery 依赖。

countries.reduce(function(l, r){
  return l.then(function(){return getData(r)});
}, $.Deferred().resolve());

如果你喜欢使用 q.js:

//create a closure for each call
function getCountry(c){return function(){return getData(c)};}
//fire the closures one by one
//note: in Q, when(p1,f1) is the static version of p1.then(f1)
countries.map(getCountry).reduce(Q.when, Q());

原答案:

又一根管子;不适合胆小的人,但更紧凑一点:

countries.reduce(function(l, r){
  return l.pipe(function(){return getData(r)});
}, $.Deferred().resolve());

减少文档可能是开始了解上述代码如何工作的最佳位置。基本上,它需要两个参数,一个回调和一个初始值。

回调迭代地应用于数组的所有元素,其中第一个参数是前一次迭代的结果,第二个参数是当前元素。这里的技巧是getData()返回一个jquery deferred promise,并且管道确保在当前元素上调用 getData 之前完成前一个元素的 getData 。

第二个参数$.Deferred().resolve()是已解析延迟值的惯用语。它被馈送到回调执行的第一次迭代,并确保立即调用第一个元素上的 getData。

于 2013-03-02T07:36:53.707 回答
4

我在 jQuery 队列方面取得了成功。

$(function(){
    $.each(countries, function(i,country){
      $('body').queue(function() {
        getData(country);
      });
    });
});

var getData = function(country){
  $.ajax({
    url : 'ajax.jsp',
    data : { country : country },
    type : 'post',
    success : function() {                          
      // Que up next ajax call
      $('body').dequeue();
    },
    error : function(){
      $('body').clearQueue();
    }
  });
};
于 2013-07-03T17:32:44.583 回答
4

我知道我迟到了,但我相信你的原始代码大部分都很好,但有两个(也许三个)问题。

getData(country)由于您对管道参数的编码方式,您会立即被调用。您拥有它的方式getData()是立即执行,结果(ajax 的承诺,但 http 请求立即开始)作为参数传递给pipe(). 因此,不是传递回调函数,而是传递一个对象——这会导致管道的 new deferred 立即被解析。

我认为它需要

deferred.pipe(function () { return getData(country); });

现在它是一个回调函数,当管道的 parent deferred 已解决时将被调用。以这种方式编码会引发第二个问题。在解决了 master deferred 之前,不会执行任何 getData()。

潜在的第三个问题可能是,由于您的所有管道都将连接到 master deferred,因此您实际上并没有链,我想知道它是否可以同时执行它们。文档说回调是按顺序执行的,但是由于您的回调返回一个承诺并异步运行,它们可能仍然在某种程度上并行执行。

所以,我认为你需要这样的东西

var countries = ["US", "CA", "MX"];
var deferred = $.Deferred();
var promise = deferred.promise();

$.each(countries, function(index, country) {
    promise = promise.pipe(function () { return getData(country); });
});

deferred.resolve();
于 2012-09-19T18:37:12.053 回答
4

我不完全确定您为什么要这样做,但保留一份您需要请求的所有 URL 的列表,并且在success调用您的函数之前不要请求下一个。IE,success将有条件地对deferred.

于 2011-12-23T06:29:21.637 回答
2

更新: deferred.pipe 已弃用

这是 jQuery API 中已经记录的大量代码。见http://api.jquery.com/deferred.pipe/

你可以一直用管道输送它们,直到所有 100 个都完成。

或者,我写了一些东西来进行 N 次调用,并使用已进行的所有调用的数据解析一个函数。注意:它返回的不是超级 XHR 对象的数据。 https://gist.github.com/1219564

于 2012-02-13T02:39:16.007 回答