从1.5开始,JQuery引入了Deferred对象,应用这个对象,针对一个行为可以注册多个回调函数,并能将行为的调用结果进行传递。以下用一些例子来说明这个对象的强大功能。
楔子:
以下的代码是用来获取一个文件的内容,获取完毕后弹出文件的内容。
<html> <head> <title></title> <script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script> </head> <body> <script type="text/javascript" language="javascript">
$(function () { $.get( "content.txt", function (resp) { alert(resp); } ); });
</script> </body> </html> |
执行结果如下:
1. 延迟调用
以上的这段程序,如果我不想马上执行,而是想在这里注册一下,在将来的某个时候让他执行。如果用传统的方法很难实现,这里Deferred的对象的优势就显示出来了。请看下面的代码:
var deferred = $.Deferred(); deferred.done(function () { $.get( "content.txt", function (resp) { alert(resp); } ); });
alert("other things...")
deferred.resolve(); |
上面的代码中,我先将要执行的代码写上,然后去做其他的事情。等到我想要这段代码执行的时候,只要调用 deferred.resolve();就可以了。
当然,也可以将这个deferred对象做为参数传递到其他地方,在其他地方调用deferred.resolve()来让这段代码执行。
我们可以把Deferred对象想象成一颗定时炸弹,把它埋在一个地方,以后我们想让它爆炸的时候,只要调用一下deferred.resolve()。
2. deferred.reject
deferred.reject与deferred.resolve正好相反,后者是让指定的行为正常执行,而前者将指定的行为拒绝,不让它执行。请看下面的代码:
var deferred = $.Deferred(); deferred.then(function () { $.get( "content.txt", function (resp) { alert(resp); } ); },
function () { alert("failed"); } );
//alert("other things...")
deferred.reject(); |
执行的结果是弹出failed.
可以看出,reject以后,会发送到failCallbacks回调函数中。
3. deferred.done()和deferred.then()
deferred.done()和deferred.then()都是在Deferred对象被Resolve或者rejected的时候调用的。请看下面的代码:
var deferred = $.Deferred(); deferred.done(function () { alert("done"); }); deferred.then(function () { alert("then"); });
deferred.resolve(); |
执行结果是先弹出done,后弹出then.
如果我们把done和then的顺序调换一下:
var deferred = $.Deferred(); deferred.then(function () { alert("then"); }); deferred.done(function () { alert("done"); });
deferred.resolve(); |
这次的执行结果就是先弹出then,后弹出done。
看来deferred.then()和deferred.then()谁放在前面谁先执行。
deferred.done()和deferred.then()都可以附加多个回调函数,请看下面的代码(注意中括号的使用):
function doneFunc1() { alert("doneFunc1"); }
function doneFunc2() { alert("doneFunc2"); }
function thenFunc1() { alert("thenFunc1"); }
function thenFunc2() { alert("thenFunc2"); }
var deferred = $.Deferred(); deferred.then([thenFunc1,thenFunc2]); deferred.done([doneFunc1,doneFunc2]);
deferred.resolve(); |
deferred.done()和deferred.then()两者的用法很相似,唯一的区别就是参数不同:
deferred.then( doneCallbacks, failCallbacks )
deferred.done( doneCallbacks [, doneCallbacks] )
从函数定义可以看出,deferred.then()可以添加FailCallBack,而deferred.done不能。
4. deferred.fail
deferred.fail用来添加一个处理事件,该处理事件在Deferred对象被reject时执行。请看下面的例子:
var deferred = $.Deferred(); deferred.fail(function () { alert("fail!"); });
deferred.reject(); |
5. deferred.always
deferred.done()和deferred.then()在deferred对象的行为成功时执行,deferred.fail在deferred对象的行为失败时执行,而deferred.always则在所有的时候都执行。
用这四个函数,我们可以模拟传统编程中的try,catch, finally.请看下面的例子:
var deferred = $.Deferred(); deferred.done(function () { alert("try 1"); }); deferred.then(function () { alert("try 2"); }); deferred.fail(function () { alert("catch"); }); deferred.always(function () { alert("finally"); }); |
以上的代码,当我们分别调用deferred.resolve()和deferred.reject()时,会看到与try,catch,finally同样的效果。
6. deferred.notify
deferred.resolve和deferred.reject都属于终结性的行为,也就是说,调用了deferred.resolve和deferred.reject后,再也不能对deferred对象进行状态上的改变(resolve, reject, notify, resolveWith, rejectWith, 和 notifyWith)。下面的例子中,第二个reject不会执行。
var deferred = $.Deferred(); deferred.done(function () { alert("done"); }); deferred.fail(function () { alert("fail"); });
deferred.resolve(); deferred.reject(); |
如果我们想让deferred对象做一些事情,而又不想终结该deferred对象,该怎么办哪?这就是deferred.notify方法的作用。请看下面的例子:
var deferred = $.Deferred(); deferred.progress(function (args) { alert(args); });
deferred.notify("notify1"); deferred.notify("notify2"); deferred.resolve(); |
这个例子的执行结果会依次弹出notify1和notify2.
7. deferred.progress
deferred.progress方法在第6条的例子中已经见到过,它就是和deferred.notify配对使用。
8. deferred.promise
有这样一种情况:我们暴露出一个Deferred对象给其他函数使用,但是只想让用户向上面挂回调函数,不想让用户改变这个Deferred对象的状态。就好像电视节目,很多人都可以定制,但是何时播放什么节目,观众个人是不能改变的。这就是deferred.promise方法的作用。
deferred.promise方法返回的Promise对象,允许其他函数添加回调函数 (done, fail, always, pipe, progress, 和 state) ,但是其他函数不能改变这个对象的状态 (resolve, reject, notify, resolveWith, rejectWith, 和 notifyWith) 。
请看下面的例子:
function GetPromise() { var deferred = $.Deferred();
deferred.resolve(); return deferred.promise();
}
var promise = GetPromise(); promise.done(function () { alert("promise.done"); }); promise.always(function () { alert("promise.always"); }); |
9. deferred.isRejected 和 deferred.isResolved
deferred.isRejected 和 deferred.isResolved是用来指示Deferred对象是被resolve了还是reject了。
var deferred = $.Deferred(); deferred.then(function () { $.get( "content.txt", function (resp) { alert(resp); } ); } );
deferred.reject();
alert(deferred.isResolved()); |
以上的代码,执行结果是弹出false
deferred.isRejected 和 deferred.isResolved在JQuery1.5加入,从JQuery1.7开始,使用deferred.state()代替。
10. deferred.pipe
deferred.resolve,deferred.reject等函数中都可以传递参数,比如下面的例子:
var deferred = $.Deferred(); deferred.done(function (arg) { alert(arg); });
deferred.resolve( 5 ); |
对于这个例子,如果我们想让参数在传递给回调函数之前进行一些转换,该怎么办哪?这就是deferred.pipe的作用。pipe顾名思义是管道的意思,就是让参数(在这里例子中是5)经过一个管道,然后到达回调函数。在这个管道中,可以对这个参数进行任意加工处理。这里需要注意的是,这里的管道是我们另外接出来的一个分支,而不会影响到主管道。
请看下面的例子:
var deferred = $.Deferred(); deferred.done(function (arg) { alert(arg); }); var filtered = deferred.pipe(function (arg) { return arg + "(processed by pipe)" }); filtered.done(function (arg) { alert("in filtered:" + arg); }); deferred.resolve(5); |
这个例子会弹出两次,一次弹出5,一次弹出in filtered:5(processed by pipe),展示了主管道和我们自己接出来的管道(filtered)的回调函数的两种不同执行结果。