如何使用 jquery 链接 ajax 调用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8612894/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 10:46:04  来源:igfitidea点击:

How to chain ajax calls using jquery

jqueryajaxjquery-deferreddeferred

提问by Graham

I need to make a series of N ajax requests without locking the browser, and want to use the jquery deferred object to accomplish this.

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

Here is a simplified example with three requests, but my program may need to queue up over 100 (note that this is not the exact use case, the actual code does need to ensure the success of step (N-1) before executing the next step):

这是一个包含三个请求的简化示例,但我的程序可能需要排队超过 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 + "]");
        }
    });

}

Here is what gets written into the console (all requests are made in parallel and the response time is directly proportional to the size of the data for each country as expected:

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

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

How can I get the deferred object to queue these up for me? I've tried changing done to pipe but get the same result.

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

Here is the desired result:

这是想要的结果:

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

Edit:

编辑:

I appreciate the suggestion to use an array to store request parameters, but the jquery deferred object has the ability to queue requests and I really want to learn how to use this feature to its full potential.

我很欣赏使用数组来存储请求参数的建议,但 jquery 延迟对象具有对请求进行排队的能力,我真的很想学习如何充分利用此功能。

This is effectively what I'm trying to do:

这实际上是我想要做的:

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

However, I want to assign the requests into the pipe one step at a time in order to effectively use the each traversal:

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

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

采纳答案by nikoshr

With a custom object

使用自定义对象

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

Fiddle http://jsfiddle.net/7kuX9/1/

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

To be a bit more clear, the last lines could be written

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

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

With pipes

带管

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/1/

Edit : A fiddle outputting the log in the result window http://jsfiddle.net/k8aUj/3/

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

Each pipe call returns a new promise, which is in turn used for the next pipe. Note that I only provided the sccess function, a similar function should be provided for failures.

每个管道调用都会返回一个新的承诺,该承诺又用于下一个管道。请注意,我只提供了 sccess 函数,应该为失败提供类似的函数。

In each solution, the Ajax calls are delayed until needed by wrapping them in a function and a new promise is created for each item in the list to build the chain.

在每个解决方案中,Ajax 调用都被延迟到需要时,将它们包装在一个函数中,并为列表中的每个项目创建一个新的 Promise 以构建链。

I believe the custom object provides an easier way to manipulate the chain, but the pipes could better suit your tastes.

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

Note: as of jQuery 1.8, deferred.pipe()is deprecated, deferred.thenreplaces it.

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

回答by topkara

Note: As of jquery 1.8 you can use .theninstead of .pipe. The .thenfunction now returns a new promise and .pipeis deprecated since it is no longer needed. See promises specfor more info about promises, and the q.jsfor a cleaner library of javascript promises without a jquery dependency.

注:在jQuery 1.8,你可以使用.then的替代.pipe。该.then函数现在返回一个新的 Promise 并且.pipe已被弃用,因为不再需要它。有关Promise 的更多信息,请参阅promises 规范,请参阅q.js以获得更清晰的 javascript 承诺库,而无需 jquery 依赖。

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

and if you like to use q.js:

如果你喜欢使用 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());

Original answer:

原答案:

Yet another pipe; not for the faint hearted, but a little bit more compact:

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

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

Reduce documentationis probably the best place to start understanding how the above code works. Basically, it takes two arguments, a callback and an initial value.

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

The callback is applied iteratively over all elements of the array, where its first argument is fed the result of the previous iteration, and the second argument is the current element. The trick here is that the getData()returns a jquery deferred promise, and the pipe makes sure that before the getData is called on the current element the getData of the previous element is completed.

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

The second argument $.Deferred().resolve()is an idiom for a resolved deferred value. It is fed to the first iteration of the callback execution, and makes sure that the getData on the first element is immediately called.

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

回答by Jeff Shepler

I know I'm late to this, but I believe your original code is mostly fine but has two (maybe three) problems.

我知道我迟到了,但我相信您的原始代码基本没问题,但有两个(可能是三个)问题。

Your getData(country)is being called immediately because of how you coded your pipe's parameter. The way you have it, getData()is executing immediately and the result (the ajax's promise, but the http request begins immediately) is passed as the parameter to pipe(). So instead of passing a callback function, you're passing an object - which causes the pipe's new deferred to be immediately resolved.

getData(country)被称为马上,因为你是如何编码你管的参数。您拥有它的方式getData()是立即执行,结果(ajax 的承诺,但 http 请求立即开始)作为参数传递给pipe(). 因此,不是传递回调函数,而是传递一个对象 - 这会导致管道的新延迟立即解决。

I think it needs to be

我认为有必要

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

Now it's a callback function that will be called when the pipe's parent deferred has been resolved. Coding it this way will raise the second problem. None of the getData()s will execute until the master deferred is resolved.

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

The potential third problem could be that since all your pipes would be attached to the master deferred, you don't really have a chain and I'm wondering if it might execute them all at the same time anyways. The docs say the callbacks are executed in order, but since your callback returns a promise and runs async, they might all still execute somewhat in parallel.

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

So, I think you need something like this

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

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

回答by ziesemer

I'm not exactly sure why you would want to do this, but keep a list of all of the URLs that you need to request, and don't request the next one until your successfunction is called. I.E., successwill conditionally make additional calls to deferred.

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

回答by Daniel Bardi

I've had success with jQuery queues.

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

回答by Drew

Update:deferred.pipe is deprecated

更新:deferred.pipe 已弃用

This is a lot of code for something that is already documented in the jQuery API. see http://api.jquery.com/deferred.pipe/

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

You can just keep piping them until all 100 are made.

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

Or, I wrote something to make N calls, and resolve a single function with the data of all the calls that have been made. Note: it returns the data not the super XHR object. https://gist.github.com/1219564

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