jQuery的开发速度很快,几乎每半年一个大版本,每两个月一个小版本。
每个版本都会引入一些新功能。今天我想介绍的,就是从jQuery 1.5.0版本开始引入的一个新功能----deferred对象。
这个功能很重要,未来将成为jQuery的核心方法,它彻底改变了如何在jQuery中使用ajax。为了实现它,jQuery的全部ajax代码都被改写了。但是,它比较抽象,初学者很难掌握,网上的教程也不多。所以,我把自己的学习笔记整理出来了,希望对大家有用。
本文不是初级教程,针对的读者是那些已经具备jQuery使用经验的开发者。如果你想了解jQuery的基本用法,请阅读我编写的《jQuery设计思想》和《jQuery最佳实践》。
======================================
jQuery的deferred对象详解
作者:阮一峰
一、什么是deferred对象?
开发网站的过程中,我们经常遇到某些耗时很长的javascript操作。其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们都不是立即能得到结果的。
通常的做法是,为它们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。
但是,在回调函数方面,jQuery的功能非常弱。为了改变这一点,jQuery开发团队就设计了deferred对象。
简单说,deferred对象就是jQuery的回调函数解决方案。在英语中,defer的意思是"延迟",所以deferred对象的含义就是"延迟"到未来某个点再执行。
它解决了如何处理耗时操作的问题,对那些操作提供了更好的控制,以及统一的编程接口。它的主要功能,可以归结为四点。下面我们通过示例代码,一步步来学习。
二、ajax操作的链式写法
首先,回顾一下jQuery的ajax操作的传统写法:
$.ajax({
url: "test.html",
success: function(){
alert("哈哈,成功了!");
},error:function(){
alert("出错啦!");
}});
(运行代码示例1)
在上面的代码中,$.ajax()接受一个对象参数,这个对象包含两个方法:success方法指定操作成功后的回调函数,error方法指定操作失败后的回调函数。
$.ajax()操作完成后,如果使用的是低于1.5.0版本的jQuery,返回的是XHR对象,你没法进行链式操作;如果高于1.5.0版本,返回的是deferred对象,可以进行链式操作。
现在,新的写法是这样的:
$.ajax("test.html")
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
(运行代码示例2)
可以看到,done()相当于success方法,fail()相当于error方法。采用链式写法以后,代码的可读性大大提高。
三、指定同一操作的多个回调函数
deferred对象的一大好处,就是它允许你自由添加多个回调函数。
还是以上面的代码为例,如果ajax操作成功后,除了原来的回调函数,我还想再运行一个回调函数,怎么办?
很简单,直接把它加在后面就行了。
$.ajax("test.html")
.done(function(){ alert("哈哈,成功了!");} )
.fail(function(){ alert("出错啦!"); } )
.done(function(){ alert("第二个回调函数!");} );
(运行代码示例3)
回调函数可以添加任意多个,它们按照添加顺序执行。
四、为多个操作指定回调函数
deferred对象的另一大好处,就是它允许你为多个事件指定一个回调函数,这是传统写法做不到的。
请看下面的代码,它用到了一个新的方法$.when():
$.when($.ajax("test1.html"), $.ajax("test2.html"))
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
(运行代码示例4)
这段代码的意思是,先执行两个操作$.ajax("test1.html")和$.ajax("test2.html"),如果都成功了,就运行done()指定的回调函数;如果有一个失败或都失败了,就执行fail()指定的回调函数。
五、普通操作的回调函数接口(上)
deferred对象的最大优点,就是它把这一套回调函数接口,从ajax操作扩展到了所有操作。也就是说,任何一个操作----不管是ajax操作还是本地操作,也不管是异步操作还是同步操作----都可以使用deferred对象的各种方法,指定回调函数。
我们来看一个具体的例子。假定有一个很耗时的操作wait:
var wait = function(){
var tasks = function(){
alert("执行完毕!");
};
setTimeout(tasks,5000);
};
我们为它指定回调函数,应该怎么做呢?
很自然的,你会想到,可以使用$.when():
$.when(wait())
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
(运行代码示例5)
但是,这样写的话,done()方法会立即执行,起不到回调函数的作用。原因在于$.when()的参数只能是deferred对象,所以必须对wait()进行改写:
var dtd = $.Deferred(); // 新建一个deferred对象
var wait = function(dtd){
var tasks = function(){
alert("执行完毕!");
dtd.resolve(); // 改变deferred对象的执行状态
};
setTimeout(tasks,5000);
return dtd;
};
现在,wait()函数返回的是deferred对象,这就可以加上链式操作了。
$.when(wait(dtd))
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
(运行代码示例6)
wait()函数运行完,就会自动运行done()方法指定的回调函数。
六、deferred.resolve()方法和deferred.reject()方法
如果仔细看,你会发现在上面的wait()函数中,还有一个地方我没讲解。那就是dtd.resolve()的作用是什么?
要说清楚这个问题,就要引入一个新概念"执行状态"。jQuery规定,deferred对象有三种执行状态----未完成,已完成和已失败。如果执行状态是"已完成"(resolved),deferred对象立刻调用done()方法指定的回调函数;如果执行状态是"已失败",调用fail()方法指定的回调函数;如果执行状态是"未完成",则继续等待,或者调用progress()方法指定的回调函数(jQuery1.7版本添加)。
前面部分的ajax操作时,deferred对象会根据返回结果,自动改变自身的执行状态;但是,在wait()函数中,这个执行状态必须由程序员手动指定。dtd.resolve()的意思是,将dtd对象的执行状态从"未完成"改为"已完成",从而触发done()方法。
类似的,还存在一个deferred.reject()方法,作用是将dtd对象的执行状态从"未完成"改为"已失败",从而触发fail()方法。
var dtd = $.Deferred(); // 新建一个Deferred对象
var wait = function(dtd){
var tasks = function(){
alert("执行完毕!");
dtd.reject(); // 改变Deferred对象的执行状态
};
setTimeout(tasks,5000);
return dtd;
};
$.when(wait(dtd))
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
(运行代码示例7)
七、deferred.promise()方法
上面这种写法,还是有问题。那就是dtd是一个全局对象,所以它的执行状态可以从外部改变。
请看下面的代码:
var dtd = $.Deferred(); // 新建一个Deferred对象
var wait = function(dtd){
var tasks = function(){
alert("执行完毕!");
dtd.resolve(); // 改变Deferred对象的执行状态
};
setTimeout(tasks,5000);
return dtd;
};
$.when(wait(dtd))
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
dtd.resolve();
(运行代码示例8)
我在代码的尾部加了一行dtd.resolve(),这就改变了dtd对象的执行状态,因此导致done()方法立刻执行,跳出"哈哈,成功了!"的提示框,等5秒之后再跳出"执行完毕!"的提示框。
为了避免这种情况,jQuery提供了deferred.promise()方法。它的作用是,在原来的deferred对象上返回另一个deferred对象,后者只开放与改变执行状态无关的方法(比如done()方法和fail()方法),屏蔽与改变执行状态有关的方法(比如resolve()方法和reject()方法),从而使得执行状态不能被改变。
请看下面的代码:
var dtd = $.Deferred(); // 新建一个Deferred对象
var wait = function(dtd){
var tasks = function(){
alert("执行完毕!");
dtd.resolve(); // 改变Deferred对象的执行状态
};
setTimeout(tasks,5000);return dtd.promise(); // 返回promise对象
};
var d = wait(dtd); // 新建一个d对象,改为对这个对象进行操作
$.when(d)
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
d.resolve(); // 此时,这个语句是无效的
(运行代码示例9)
在上面的这段代码中,wait()函数返回的是promise对象。然后,我们把回调函数绑定在这个对象上面,而不是原来的deferred对象上面。这样的好处是,无法改变这个对象的执行状态,要想改变执行状态,只能操作原来的deferred对象。
不过,更好的写法是allenm所指出的,将dtd对象变成wait()函数的内部对象。
var wait = function(dtd){
var dtd = $.Deferred(); //在函数内部,新建一个Deferred对象
var tasks = function(){
alert("执行完毕!");
dtd.resolve(); // 改变Deferred对象的执行状态
};
setTimeout(tasks,5000);return dtd.promise(); // 返回promise对象
};
$.when(wait())
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
(运行代码示例10)
八、普通操作的回调函数接口(中)
另一种防止执行状态被外部改变的方法,是使用deferred对象的建构函数$.Deferred()。
这时,wait函数还是保持不变,我们直接把它传入$.Deferred():
$.Deferred(wait)
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
(运行代码示例11)
jQuery规定,$.Deferred()可以接受一个函数名(注意,是函数名)作为参数,$.Deferred()所生成的deferred对象将作为这个函数的默认参数。
九、普通操作的回调函数接口(下)
除了上面两种方法以外,我们还可以直接在wait对象上部署deferred接口。
var dtd = $.Deferred(); // 生成Deferred对象
var wait = function(dtd){
var tasks = function(){
alert("执行完毕!");
dtd.resolve(); // 改变Deferred对象的执行状态
};
setTimeout(tasks,5000);
};
dtd.promise(wait);
wait.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
wait(dtd);
(运行代码示例12)
这里的关键是dtd.promise(wait)这一行,它的作用就是在wait对象上部署Deferred接口。正是因为有了这一行,后面才能直接在wait上面调用done()和fail()。
十、小结:deferred对象的方法
前面已经讲到了deferred对象的多种方法,下面做一个总结:
(1) $.Deferred() 生成一个deferred对象。
(2) deferred.done() 指定操作成功时的回调函数
(3) deferred.fail() 指定操作失败时的回调函数
(4) deferred.promise() 没有参数时,返回一个新的deferred对象,该对象的运行状态无法被改变;接受参数时,作用为在参数对象上部署deferred接口。
(5) deferred.resolve() 手动改变deferred对象的运行状态为"已完成",从而立即触发done()方法。
(6)deferred.reject() 这个方法与deferred.resolve()正好相反,调用后将deferred对象的运行状态变为"已失败",从而立即触发fail()方法。
(7) $.when() 为多个操作指定回调函数。
除了这些方法以外,deferred对象还有二个重要方法,上面的教程中没有涉及到。
有时为了省事,可以把done()和fail()合在一起写,这就是then()方法。
$.when($.ajax( "/main.php" ))
.then(successFunc, failureFunc );
如果then()有两个参数,那么第一个参数是done()方法的回调函数,第二个参数是fail()方法的回调方法。如果then()只有一个参数,那么等同于done()。
这个方法也是用来指定回调函数的,它的作用是,不管调用的是deferred.resolve()还是deferred.reject(),最后总是执行。
$.ajax( "test.html" )
.always( function() { alert("已执行!");} );
(致谢:本文第一稿发表后,allenm来信指出原文对promise()的理解是错的。现在的第二稿是根据他的文章修改的,在此我表示衷心感谢。)
(完)
qq 说:
请问博主,第六条中的 wait函数还是保持不变,是保持第一个版本不变还是保持添加了dtd参数的版本不变?
2011年8月17日 07:03 | # | 引用
锐齿猱 说:
我开始对这里也疑问,不过可以看 代码示例6。
2011年8月17日 08:29 | # | 引用
苗苗老师 说:
阮兄是做什么工作的?每天都有时间研究这些,是在政府机关吧?
2011年8月17日 10:34 | # | 引用
TBAG 说:
真是变化好大啊。新的方式写的代码很fluent...
2011年8月17日 14:34 | # | 引用
david 说:
dojo早就有了。
2011年8月17日 15:33 | # | 引用
阿吉 说:
刚看API呢。学习啦。
2011年8月17日 15:35 | # | 引用
hileon 说:
node.js 里有与jquery的deferred对象等价的对象吗?
2011年8月17日 18:27 | # | 引用
白洁 说:
博主您好,
我看了这篇文章有几点感悟不知道是不是对的.1、用deferred可以简化$.ajax,但是deferred也没有丰满$.ajax的功能.因为$.ajax同样有seccess,error的方法,而且方法里面 你还能调用其他方法.success:myfunction 这样也能复用.而且$.ajax也有异步或同步的属性.2、deferred的出现解决了一个非ajax请求的监听事件是否执行成功的一系列方法。
不知道我的看法是不是对的。还请博主指教。
2011年8月17日 22:39 | # | 引用
阮一峰 说:
@白洁:
我的理解是,deferred主要解决回调函数的部署,它与$.ajax方法的设计各有侧重。
趋势是$.ajax将来会完全使用deferred,作为自己的回调函数接口。有文章称,success方法将在1.8.0版本被取消。
2011年8月18日 12:19 | # | 引用
贝壳里的海 说:
你的博客访问速度好强大啊,太快了
2011年8月18日 12:33 | # | 引用
seasunk 说:
我怎么觉得还是原来的做得顺手~~
2011年8月18日 15:44 | # | 引用
徐伟思 说:
2011年8月18日 23:05 | # | 引用
我爱pc 说:
阮兄弟的主机在香港。
2011年8月19日 09:27 | # | 引用
gwallan 说:
请教楼主,有什么方法能让在then里面产生的数据传递到done中,比如:
$.when(function(){
}).then(function(){
var arg = '';
}).done(function(){
//如何得到上面arg参数?
});
2011年8月21日 18:04 | # | 引用
阮一峰 说:
把数据绑在this关键字上面。
2011年8月21日 20:15 | # | 引用
snowqiang 说:
Jquery简直就像封装了一个语言层面的异步语法,创建并就绪,执行,或成功或失败,最后再加个处理.如果能再加上一个主动超时处理就更强了.
2011年8月25日 17:53 | # | 引用
自由过度 说:
dtd.promise()的目的,就是保证目前的执行状态----也就是"未完成"----不变,从而确保只有操作完成后,才会触发回调函数。
这个还是删掉吧,文章作者根本没理解什么是promise只是从字面上去推定这个函数的意义
2011年8月26日 18:56 | # | 引用
Hyacinthus, Chu 说:
其实我们可以看到,var d = $.Deferred(),与 var p = d.promise(); 这两者的区别主要就在于, d 包含了 resolve, resolveWith, reject 与 rejectWith 这四个方法。而这四个方法就是用来触发 done, fail, always 这些个回调函数的。
之所以要返回 d.promise(): 一是因为 CommonJS promise/A 本来就应当是这样子的;二也是用来避免返回的对象能够主动地调用到resolve与reject这些关键性的方法。
2011年9月24日 13:03 | # | 引用
Hongyy 说:
不影响原来ajax的使用,异步或同步的属性,还有传递的参数的使用方式还是和原来的一样。
2011年9月27日 11:50 | # | 引用
bailey 说:
受益匪浅,比官方的解释好
2011年10月27日 11:44 | # | 引用
jfbcb 说:
今天看到1.7出来,就找教程看看,阮兄弟的文章不错.顶一个,主机放HK就是好,我的移到ix,尼玛.百度清站了,据说是不能正常打开..坑爹..
2011年11月10日 15:45 | # | 引用
ifree 说:
... node是js引擎,jQuery是支持的.
2011年11月18日 01:59 | # | 引用
molayu 说:
node是“宿主环境”(我理解为JVM子类的),它是由V8引擎来执行JS,并实现了CommonJS标准库,赋予JS更多强大的能力。
2012年1月 4日 11:30 | # | 引用
唇角轻扬 说:
看得晕乎乎的
2012年1月30日 22:32 | # | 引用
A_Hunt 说:
dtd,虽然是个局部变量,但还是建议改一下吧,应该是:dfd 吧。dtd在这里莫名其妙了。
2012年3月 9日 16:38 | # | 引用
TooBug 说:
受益匪浅!
但是第八点有误
$.Deferred(wait)
这里的wait和上面不一样,在wait再定义一个dtd将不能正常工作。
2012年3月14日 20:27 | # | 引用
小小僧 说:
如果success被取消了,
success:function(rel){//处理rel}
那之前的ajax返回的数据怎么传递给done()?
2012年4月26日 15:14 | # | 引用
vivi 说:
很受益,谢谢
2012年5月30日 16:34 | # | 引用
陈希章 说:
相当不错的文章,感谢
2012年8月 7日 10:21 | # | 引用
陈希章 说:
我觉得Deferred这个功能,并不能真正解决对本地非异步方法的封装,因为javascript是单线程的,如果该方法需要很长时间执行,例如下面这样一个函数
var array = new Array();
var i = 1;
while (i array.push(i);
array = array.reverse();
i++;
}
那么,在执行这个函数的时候,整个页面还是被卡住的。即便可以通过本文所说的技术用Deffered。
有没有什么方法将这种长时间的方法,真正实现出来异步的效果,做完之后再调用回调函数即可。
请阮兄指教
2012年8月 7日 11:34 | # | 引用
KentZen 说:
致谢为后来者提供有用知识的先驱!
2012年8月15日 16:36 | # | 引用
snowqiang 说:
第七节的最后一个示例代码
"var wait = function(dtd){"
应为:
"var wait = function(){"
2012年8月18日 14:04 | # | 引用
韭菜 说:
好文啊,收藏了,非常有用。
这里 http://stackoverflow.com/questions/5111695/jquery-jqxhr-cancel-chained-calls-trigger-error-chain 也有介绍
2012年9月29日 13:13 | # | 引用
祁涛 说:
使用deferred对象写JS动画非常方便,例如:
// Animation flows.
$.when( preloadImage() )
.then( animation01 )
.then( animation02 )
.then( animation03 )
.then( transition )
.then( merge )
.then( zoom )
.then( showContent )
.then( flicker );
优点:
1、可以任意调整动画的先后顺序。
2、添加 SKIP(跳过动画)功能也很方便。
3、调试动画也可以节省大量时间。 把不需要调试的动画项注释掉。 例如:
$.when( preloadImage() )
// .then( animation01 )
// .then( animation02 )
// .then( animation03 )
// .then( transition )
// .then( merge )
// .then( zoom )
.then( showContent )
.then( flicker );
2012年12月 6日 18:00 | # | 引用
Jease 说:
其实我一直觉得那个 dtd 的变量有歧义,还是叫 dfd 比较合适。
2013年1月 9日 11:36 | # | 引用
roc 说:
很感谢,一看就懂,现学现用
2013年2月19日 11:15 | # | 引用
doyle 说:
$.when(f1).then(f2)
保证了f2作为f1的回调函数,在f1之后执行?
deffered对象果然只是回调函数解决方案,f1阻塞的时候照样锁死浏览器
2013年2月20日 11:36 | # | 引用
未来的前端 说:
看完之后豁然开朗,同时,网站的设计很好看!
2013年3月 4日 10:29 | # | 引用
ian 说:
希望不久自己能把相关源码分析一下,应用到自己的项目中。
2013年3月24日 23:33 | # | 引用
springwang 说:
呵呵,文章写的很清晰,从使用上都理解了,关于具体实现的细节我没有看jquery的源码,下面是我自己整理的一个简单关于Defered和when的实现,没有添加参数传递的部分,希望能给大家一点帮助:
地址 http://jsfiddle.net/springwang/uW6tb/9/
2013年3月26日 15:11 | # | 引用
旧城 说:
最后一例有些问题,可以直接在外部调用 dtd.resolve()。promise方法应该是仅仅扩展指定对象操作Deferred对象的部分API(主要是 done、fail、always等等,没有resolve、reject这类API),这样多个对象可以操作同一个Deferred对象。所以最后那个例子的实现还是需要跟allenm推荐的一样,必须使用函数封装。
2013年4月 9日 00:46 | # | 引用
阿良 说:
2013年4月21日 11:52 | # | 引用
菜菜 说:
学习啦,太感谢啦...
2013年5月27日 08:57 | # | 引用
McGradi 说:
如果有三个事件event01,event02,event03,要event01->event02->event03顺序执行。要怎么写?
我试了$.when(event01()).done(event02).done(event03);但是不行,event02和event03同时执行了。
http://jsfiddle.net/McGradi/PUb3Z/
2013年6月 5日 09:55 | # | 引用
杨青 说:
@阿良:
then里头的function直接return就好啦
2013年6月 5日 18:14 | # | 引用
清泉古雾 说:
一直都在关注您的博客 跟您学习了很多
最近在看jQuery源码 翻出来您这篇博客
发现代码示例10里面
"var wait = function(dtd){"
应该是
"var wait = function(){"
希望能跟您学习的更多啊
2013年7月 3日 19:13 | # | 引用
黄雪良 说:
感觉那个d = wait(dtd);改为
dtd = wait(dtd)更好。
本意是为了防止dtd.resolve()在下面被使用
2013年9月 3日 16:30 | # | 引用
song 说:
Very Good !
2013年10月 8日 10:56 | # | 引用
yanhaijing 说:
我喜欢回调的方式,不喜欢链式
2013年10月15日 14:35 | # | 引用
cnDenis 说:
谢谢博主! 读一篇博文, 比看两天手册还更清楚
很喜欢这Blog简洁明了的风格!
2013年11月28日 11:40 | # | 引用
joy 说:
阮老师的文章,真心不错,一看就懂,顶一个
2013年12月 6日 17:28 | # | 引用
邹成卓 说:
ajax用的多了,体会到defferd对象一个非常大的好处:可以很方便地动态设置回调。
比如我写了一个ajax,但是并不知道回调中会执行哪些操作,回调的回调中又会执行哪些操作。
用defferd的话,我可以什么都不写,等以后要用的时候把回调挨个往后边加就好了。 这比自己去预留一些回调的处理方便多了
2013年12月11日 21:06 | # | 引用
duan 说:
刚刚想了解deferred和promise就看到了你的大作!比网上其他的文章介绍的好多了~!
十分感谢!
2013年12月23日 09:58 | # | 引用
kan 说:
有例子可以运行,不懂得运行就可以理解了。非常感谢
2013年12月29日 11:29 | # | 引用
苍蝇和竹子 说:
jQuery规定,$.Deferred()可以接受一个函数名(注意,是函数名)作为参数
这个“函数名”怎么理解。。?
2014年3月 4日 02:09 | # | 引用
kai 说:
博主,有一个问题请教,假设有4个function,A,B,C,D,每个function都有异步调用的操作,我现在需要按顺序执行这4个function,而且要求只有前一个function执行完毕之后,下一个function才能开始。请问用 Deferred 这个函数能完成这样的功能么,如果能,还望不吝赐教
2014年3月21日 17:00 | # | 引用
lee 说:
A.then(B).then(C).then(D)
2014年3月27日 15:42 | # | 引用
kai 说:
这样恐怕不行,A,B,C,D是四个异步函数,你可以自己试试
2014年3月30日 10:35 | # | 引用
希格斯粒子 说:
不过,更好的写法是allenm所指出的,将dtd对象变成wait()函数的内部对象。
var wait = function(dtd){
这句代码function里面的dtd是多余的吧?
2014年4月 4日 11:08 | # | 引用
lee 说:
http://jsfiddle.net/yCv8S/
2014年4月 4日 16:30 | # | 引用
vajoy 说:
是dfd不是dtd吧? 0。0
2014年4月30日 20:55 | # | 引用
高军 说:
var wait = function (dfd) {
var task = function () {
alert('completed!!');
dfd.resolve();
}
setTimeout(task, 5000);
}
$.Deferred(wait).done(function () {
alert('done')
});
个人觉得这种方式比较简单,也相对好理解;
另外,对于$.Deferred().promise();最后那句总结很明确了---> 没有参数时,返回一个新的deferred对象,该对象的运行状态无法被改变;接受参数时,作用为在参数对象上部署deferred接口
2014年5月 7日 14:19 | # | 引用
谢亮 说:
赞个
2014年5月15日 13:11 | # | 引用
苏国潮 说:
你好,我现在遇到一个问题,就是怎么在执行ajax时触发 defferd.progress的回调函数?
收到留言恳请回复我的邮箱。
2014年8月18日 12:13 | # | 引用
by 说:
不错,相当精辟,深入浅出。
2014年9月 2日 09:59 | # | 引用
L.YK 说:
var dtd = $.Deferred(); // 新建一个deferred对象
var wait = function(dtd){
var tasks = function(){
alert("执行完毕!");
dtd.resolve(); // 改变deferred对象的执行状态
};
setTimeout(tasks,5000);
return dtd;
};
可以写成下面这样吧?
var dtd = $.Deferred(); // 新建一个deferred对象
var wait = function(){
var tasks = function(){
alert("执行完毕!");
dtd.resolve(); // 改变deferred对象的执行状态
};
setTimeout(tasks,5000);
};
2014年9月 4日 15:47 | # | 引用
Jacky 说:
既然是详解,建议加上如何通过resolve返回原函数的执行结果,即如何像回调函数一样传递执行结果。我搞了一下午才搞清楚。
示例:
var wait = function(dtd){
var dtd = $.Deferred(); //在函数内部,新建一个Deferred对象
var tasks = function(){
alert("执行完毕!");
dtd.resolve(new Date()); // 执行完成后,返回当前时间
};
setTimeout(tasks,5000);
return dtd.promise(); // 返回promise对象
};
$.when(wait())
.done(function(time){ alert("完成时间:" + time); })
.fail(function(){ alert("出错啦!"); });
2014年10月27日 17:24 | # | 引用
国安信徒 说:
$.Deferred(wait),这样写的话,如果我想往wait里面传递参数的话,除了换成用$.when实现的话,有什么办法?谢谢
2014年11月25日 11:32 | # | 引用
潇意 说:
跟着跑了一遍,赶脚都能懂,只是不太明白when这些函数的原理
2014年12月30日 17:54 | # | 引用
rgshare 说:
代码示例11里面的dtd变量似乎有些让人迷惑,改成this是不是要好一些?
如:
var wait = function(dtd){
var that = this;
var tasks = function(){
console.log("执行完毕!");
that.resolve(); // 改变Deferred对象的执行状态
};
setTimeout(tasks,5000);
return that.promise();
};
//...
2015年1月 5日 10:30 | # | 引用
hegfirose 说:
这个表述有问题,jQuery 官方文档:
2015年1月 9日 14:22 | # | 引用
孙成利 说:
实例8是怎么个执行顺序啊,不解
2015年1月14日 17:51 | # | 引用
zwCai 说:
2015年2月 9日 16:36 | # | 引用
YJB 说:
请教大侠一个问题:
我是用PHP+JQUERY+AJAX+MYSQL构建的网站,在一个主页面中加载了两个AJAX,也就是引入了两个.JS文件
,在具体执行时总是排在后面的$.AJAX()请求被执行,前一个不执行;也就是说这两个.JS文件谁放在后面谁执行。怎样处理让这两个请求分别执行?
2015年2月13日 10:09 | # | 引用
李培杰 说:
正在学习,很受用
2015年4月 8日 15:31 | # | 引用
zhanqi 说:
写得很清楚
2015年4月16日 15:59 | # | 引用
网名很难取 说:
能不能来点实际的案例,这样才能真正的运用起deferred对象--这个API!不然说了这么多也没有起到作用,真正会用的人不会看,不会用的人看了也不会实际运用!
2015年4月21日 16:42 | # | 引用
z-reg 说:
怎么想后台来传送数据啊?
2015年4月21日 19:05 | # | 引用
wengeezhang 说:
deferred感觉不符合标准,还是原生的Promise比较好。chrome 42已经内置Promise.http://mustrank.com/blog/Promise.html
2015年4月30日 10:57 | # | 引用
一个程序员 说:
Promise当reject时, 没有任务程序去处理, 不是就会被忽略了, 不能像throw那样, 最后报错.
这个问题是, 我在使用Promise时, 发现如果我没有调用catch(), 也不报错, 经常因为没有catch(), 没发现错误.
有什么办法可解决, 当没有catch时, 就抛出错误?
2015年6月19日 22:06 | # | 引用
jonasli 说:
hi,您好, 第七种的最后一个例子 改为了内部局部变量,为什么还要返回dtd.promise(),直接返回dtd,外边的不是也没有办法操作dtd了吗,求解?多谢
2015年7月11日 14:47 | # | 引用
戈饭 说:
看文档术语太多晕晕的,看这个一下就懂,再看文档全懂了。
果然会讲课的好老师太重要了。
2015年7月14日 16:34 | # | 引用
水墨丹青 说:
其实改为了内部局部变量之后,是不需要返回dtd.promise(),直接返回dtd就行,不过为啥要返回dtd?
2015年8月25日 16:05 | # | 引用
evilemon 说:
实例10上面的wait函数应该为形参为空
2015年10月19日 18:05 | # | 引用
Rick 说:
应该返回dtd或者promise都可以, 但必须返回,因为when的参数必须是defered对象,否则done会马上触发。
2015年10月24日 04:43 | # | 引用
渣渣 说:
打开http://jsfiddle.net里的示例不是太卡就打不开,阮大师能否把示例放到
www.jsbin.com上?
2015年12月 2日 15:10 | # | 引用
谢武 说:
运行代码9感觉没啥用,还是可以用dfd来从外面触发resolve
2015年12月 8日 17:11 | # | 引用
谢武 说:
是可以的漏看了把事件绑定在promise上那啦
2015年12月 8日 17:21 | # | 引用
whws 说:
直接执行wait函数的情况应该很少吧,大多数情况都是需要传参调用的,那如何传参呢?
大多数函数都需要返回值的,那你这里返回 dtd,其他数据怎么返回呢?
2015年12月10日 16:56 | # | 引用
w 说:
resolve,reject调用作为参数传递,done,fail接收就行、
2015年12月16日 14:23 | # | 引用
whws 说:
你说的这个是接收数据返回值。
我上面还有一个问题呢,如何给 wait 函数传参呢?
2015年12月23日 15:17 | # | 引用
下雨天顶着蘑菇 说:
刚好工作中用到这个,学习了一下,谢谢分享
2015年12月24日 16:46 | # | 引用
小武 说:
感觉 这句话不太对,因为done返回一个promise时不会被处理,并且done的参数始终是defer被resolve时的参数,而then会把当前callback的返回值做为下一个then的参数,并且then里的promise会被等待处理掉(当然可能成功或者失败了)
2016年1月 6日 19:35 | # | 引用
小武 说:
这句话是:有时为了省事,可以把done()和fail()合在一起写,这就是then()方法。
2016年1月 6日 19:36 | # | 引用
随缘 说:
@陈希章:
你可以将你的操作拆分成小的碎片,利用匿名函数与闭包,每100ms中断一次,用setTimeout方法,这样浏览器就会有足够的渲染时间了。
2016年1月10日 12:15 | # | 引用
Chan 说:
請教一下,promise 的功能,是怕 deferred 是 global 變數的時候會被外部影響,但如果是直接調用 $.Deferred 是不是就沒這個問題,像這樣寫
let roll = () => {
return $.Deferred((dfd) => {
setTimeout(() => {
dfd.resolve();
}, 2000);
});
};
$.when(roll()).then(() => {
console.log('all done');
});
2016年2月24日 08:46 | # | 引用
Gibbon 说:
// case2, alert success1, fail 1, fail 2
var dtd = $.Deferred(); // 新建一个Deferred对象
dtd.resolve('success');
dtd.then(function(msg){
alert( msg + ' 1');
return new $.Deferred().reject('fail');
}).fail(function(msg){
alert( msg + ' 1');
return new $.Deferred().resolve('success');
}).then(function(msg){
alert( msg + ' 2');
return msg;
}).fail(function(msg){
alert( msg + ' 2');
return msg;
})
这一段代码执行结果是 success1, fail 1, fail 2. 能解释一下否?
2016年2月25日 13:21 | # | 引用
好炫 说:
您好 这个写法很好 但是我有一个疑问
这个写法可以像ajax 那样获得数据吗?
$.ajax({
success:function(json){}
})
.done(function(json){}) //好像没看到相应的例子额
我发现每次都是返回一个defer对象 那数据应该怎么返回呢 求教?
2016年2月26日 15:20 | # | 引用
Sameen 说:
博主,请问最后总结那里第(4)点是否写错了, deferred.promise() 返回的是一个promise对象吧.
2016年3月10日 17:05 | # | 引用
mir 说:
function foo() {
return $.Deferred(function (defer) {
...
}).promise();
}
我一直用的是这种写法,这样的好处是原始代码极易修改,只要将原来的方法体用这两句话包裹起来,把里面原本return的部分改成defer.resolve()就可以了
2016年5月26日 16:22 | # | 引用
SCLeo 说:
难道这就是传说中的Promise >.>
2016年7月 4日 00:47 | # | 引用
zhou 说:
在【五、普通操作的回调函数接口(上)】中有一句“原因在于$.when()的参数只能是deferred对象,所以必须对wait()进行改写:”jQuery文档是这么说的 If a single argument is passed to jQuery.when() and it is not a Deferred or a Promise, it will be treated as a resolved Deferred and any doneCallbacks attached will be executed immediately.
2016年7月 6日 17:31 | # | 引用
世纪之光 说:
调理清晰,高质量文章。
不过文中提到的Deferred用于普通函数的场景(典型的大数组的遍历,不是用setTimeout模拟),应该并无多大实际意义吧?javascript的执行队列本来就是在单个线程内完成,这种耗时的计算,执行是同步的,代码顺序写应该比较合理吧,用Deferred的意义在哪里呢?
2016年8月21日 10:28 | # | 引用
陈豪 说:
then 不只是简化那么简单哦,还能做到避免回调地狱哦。
2016年9月 1日 16:07 | # | 引用
frey wang 说:
为什么大神的名字 这么熟悉,但是我又不知道从哪里得知,好熟悉
2016年9月30日 16:50 | # | 引用
Ye Zhang 说:
学了一年了,现在才知道,囧
2017年1月 4日 10:39 | # | 引用
jimmy2019 说:
第七点,“在原来的deferred对象上返回另一个deferred对象”后面的那个“deferred对象”是不是应该是“promise对象”?
2017年1月23日 20:33 | # | 引用
pyfxl 说:
感谢这么精彩的文章。
作者看一下运行实例代码10的第一行
2017年2月 3日 07:55 | # | 引用
琥珀 说:
阮老师,我在使用你的react脚手架的github工程的时候,报错了WebpackOptionsValidationError: Invalid configuration object. Webpack has been in
itialised using a configuration object that does not match the API schema.我是在windows下开发的,所以直接下了zip包,没有使用git clone,然后运行了npm install npm start 就报错了。这个错误我定位了很久,但是实在是技术不到家,没办法排查出来。请问这个到底是什么原因?
2017年2月 3日 08:21 | # | 引用
羊锡贵 说:
示例9那里,我把d.resolve改成dtd.resolve就可以改变执行状态了,所以这个示例不对。
2017年2月25日 09:35 | # | 引用
youzaiyouzai 说:
//这样写也完全没有全局的变量
!(function () {
"use strict";
var wait = function () {
var dtd = $.Deferred(); // 新建一个deferred对象
var tasks = function () {
alert("执行完毕!");
dtd.resolve(); // 改变deferred对象的执行状态
};
setTimeout(tasks, 1000);
return dtd;
};
$.when(wait())
.done(function () {
alert("哈哈,成功了!");
})
.fail(function () {
alert("出错啦!");
});
}());
2017年3月10日 14:44 | # | 引用
kaa 说:
promise的前传
2017年4月30日 09:22 | # | 引用
勿恨水长东 说:
通俗易懂,看你的博客很有收获,刚好用到
2017年5月11日 09:39 | # | 引用
Brian 说:
謝謝 你的指導文章 感恩
2017年6月10日 00:22 | # | 引用
jquery源码学习者 说:
最近在看jquery源码,结合着一起看更明白了些,评论更加精彩。这篇文章从11年到17年,精华还在,感谢博主!
2017年6月12日 17:27 | # | 引用
MO红糖 说:
虽然是其他链接进来的,但是还是直接解决了我项目中的问题!
ajax 同步阻塞导致的 UI线程阻塞问题!
2017年6月15日 15:51 | # | 引用
薛皓月 说:
上面的方法中,用$.when(d).done().fail(),可以改为直接用d.done().fail()吧。这了用$.when没有意义啊?
2017年7月13日 21:36 | # | 引用
薛皓月 说:
(下)中的方法不可行。在最后加上dtd.resolve();依然会有问题的
2017年7月13日 22:34 | # | 引用
您的大名 说:
事实证明就是虾扯蛋,现在3.x都出来了,也没见取消success
2017年9月12日 09:14 | # | 引用
ahdung 说:
fail是只要其中之一不成功就执行,那请教怎样才能allFail,就是全部失败才执行?
2018年1月12日 16:34 | # | 引用
hnayan 说:
现在还有很多程序员不知道deferred,写ajax就是success,error回调处理呢
2018年4月25日 15:38 | # | 引用
您的大名 说:
写的很好
2018年8月29日 15:08 | # | 引用
Kevin 说:
老师,请问新版的写法$.ajax("test.html")在哪里设置请求类型(get/post)呢?另外如果想带查询参数的话需要写在哪里呢?
2019年6月12日 16:53 | # | 引用
妙音天女 说:
方法八不对,返回的是deferred对象而非promise对象,还是可以通过外部resolve更改
2019年11月26日 19:54 | # | 引用
FILE_F 说:
厉害,思路清晰!
2020年3月 3日 17:25 | # | 引用
heven 说:
厉害, 跨越了9年, 从11年到20年
2020年4月12日 09:46 | # | 引用
EvanPei 说:
"既有异步的操作(比如ajax读取服务器数据)"
老师你好,如您题所说,ajax我在取后台数据时如果时间很长,怎么保证html不会卡住?
2022年2月23日 14:36 | # | 引用
arthur 说:
做一个数据加载中状态就行了
2023年1月10日 12:12 | # | 引用