如何使用jquery链接ajax调用
我需要在不locking浏览器的情况下进行一系列的N ajax请求,并且希望使用jQuery的延迟对象来实现这一点。
下面是一个有三个请求的简单例子,但是我的程序可能需要排队超过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]
我怎样才能得到延期的对象排队这些为我? 我试着改变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]
编辑:
我很欣赏使用数组来存储请求参数的build议,但jQuery的延迟对象有排队请求的能力,我真的很想学习如何充分利用这个function。
这实际上是我想要做的:
when(request[0]).pipe(request[1]).pipe(request[2])... pipe(request[N]);
但是,为了有效地使用每个遍历,我想一次一步地将请求分配到pipe道中:
deferred.pipe(request[0]); deferred.pipe(request[1]); deferred.pipe(request[2]);
用一个自定义的对象
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();} );
用pipe道
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/3/
每个pipe道调用都会返回一个新的promise,这个promise又被用于下一个pipe道。 请注意,我只提供了sccess函数,应该为失败提供类似的函数。
在每个解决scheme中,Ajax调用都被延迟到需要时,通过将它们包装在一个函数中,并为列表中的每个项目创build一个新的承诺来构build链。
我相信自定义对象提供了一个更容易的方式来操纵链,但pipe道可以更好地适合你的口味。
注意 :从jQuery 1.8开始, deferred.pipe()
是不推荐使用的, deferred.then
replace它。
注意:从jquery 1.8开始,你可以使用.pipe
而不是.pipe
。 .pipe
函数现在返回一个新的promise,而.pipe
已被弃用,因为它不再需要。 请参阅promises规范 ,了解有关承诺的更多信息,以及q.js,它提供了一个没有jQuery依赖关系的更简洁的JavaScript承诺库。
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());
减less文档可能是开始理解上面的代码如何工作的最好的地方。 基本上,它有两个参数,一个callback和一个初始值。
callback迭代地应用于数组的所有元素,其中第一个参数是前一次迭代的结果,第二个参数是当前元素。 这里的诀窍是getData()
返回一个jQuery延期承诺 ,并且pipe道确保在当前元素上调用getData之前,前一个元素的getData完成。
第二个参数$.Deferred().resolve()
是一个已parsing延迟值的习惯用法。 它被馈送到callback执行的第一次迭代,并确保第一个元素上的getData被立即调用。
我不确定你为什么要这样做,但保留一个你需要请求的所有URL的列表,并且不要求下一个,直到你的success
函数被调用。 IE, success
将有条件地使额外的电话deferred
。
我知道我迟到了,但我相信你的原代码基本上没问题,但有两个(也许是三个)问题。
你的getData(country)
正在被调用,因为你如何编码你的pipe道参数。 getData()
是立即执行,结果(ajax的承诺,但http请求立即开始)作为parameter passing给pipe()
。 所以不是传递一个callback函数,而是传递一个对象 – 这将导致pipe道的新延迟被立即解决。
我认为这是需要的
deferred.pipe(function () { return getData(country); });
现在,它是一个callback函数,当pipe道的父级延迟已被解决时,将被调用。 用这种方法编码会引起第二个问题。 getData()将不会执行,直到parsing主体。
潜在的第三个问题可能是,因为所有的pipe道都会被连接到延迟的主服务器上,所以你并不是真的有一个链,我想知道它是否可以在同一时间全部执行它们。 文档说,callback是按顺序执行的,但是由于你的callback函数返回一个promise并且运行asynchronous,所以它们可能仍然有些并行执行。
所以,我觉得你需要这样的东西
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();
更新: deferred.pipe已弃用
这是jQuery API中已经logging的很多代码。 请参阅http://api.jquery.com/deferred.pipe/
你可以保持pipe道,直到所有100个制成。
或者,我写了一些东西来进行N个调用,然后用所有已经调用的数据parsing一个函数。 注意:它返回的数据不是超级XHR对象。 https://gist.github.com/1219564
我用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(); } }); };