jquery 中的 deferred 对象
参考链接
- jQuery API中文文档
- jQuery.Deferred
- jQuery.when
- jQuery的deferred对象详解
- jQuery deferred 对象的 promise 方法
- jQuery中的Deferred-详解和使用
什么是 deferred 对象?
延迟对象,在jQuery的1.5引入,是通过调用jQuery.Deferred()方法创建一个可链式调用的工具对象。 它可以注册多个回调到回调队列, 调用回调队列,准备代替任何同步或异步函数的成功或失败状态。——jQuery API中文文档简单说,deferred对象就是jQuery的回调函数解决方案。在英语中,defer的意思是"延迟",所以deferred对象的含义就是"延迟"到未来某个点再执行。它解决了如何处理耗时操作的问题,对那些操作提供了更好的控制,以及统一的编程接口。——阮一峰deferred 对象的主要功能
ajax 操作的链式写法
$.ajax("test.html") .done(function(){ alert("success"); }) .fail(function(){ alert("error"); });
$.ajax()
操作完成后,如果使用的是低于1.5.0版本的jQuery,返回的是XHR对象,无法进行链式操作;如果是高于1.5.0版本,返回的是deferred对象,可以进行链式操作。可以看到,done()
相当于success
方法,fail()
相当于error
方法。采用链式写法以后,代码的可读性大大提高。这里着重强调一下 jqXHR 对象,从 jQuery 1.5 开始,
从 jQuery 1.5 开始,$.ajax()返回的jqXHR对象 实现了 Promise 接口, 使它拥有了 Promise 的所有属性,方法和行为。(见Deferred object获取更多信息)。为了让回调函数的名字统一,便于在$.ajax()中使用。jqXHR也提供.error() .success()和.complete()方法。这些方法都带有一个参数,该参数是一个函数,此函数在 $.ajax()请求结束时被调用,并且这个函数接收的参数,与调用 $.ajax()函数时的参数是一致。这将允许你在一次请求时,对多个回调函数进行赋值,甚至允许你在请求已经完成后,对回调函数进行赋值(如果该请求已经完成,则回调函数会被立刻调用)。$.ajax()
返回的 jqXHR对象 本身就是 deferred 对象,因此可以像上面代码中那样进行链式调用。注意事项:
jqXHR.success()
,jqXHR.error()
, 和jqXHR.complete()
回调从 jQuery 1.8开始 被弃用过时,从jQuery 3.0开始被删除,你可以使用jqXHR.done()
,jqXHR.fail()
, 和jqXHR.always()
代替。指定同一操作的多个回调函数
deferred 对象的一大好处,就是它允许你自由添加多个回调函数。还是以上面的代码为例,如果ajax操作成功后,除了原来的回调函数,我还想再运行一个回调函数,怎么办?很简单,直接把它加在后面就行了。$.ajax("test.html") .done(function(){ alert('success'); }) .fail(function(){ alert('error'); }) .done(function(){ alert('第二个回调函数!'); });
回调函数可以添加任意多个,它们按照添加顺序执行。
为多个操作指定回调函数
deferred 对象的另一大好处,就是它允许你为多个事件指定一个回调函数,这是传统写法做不到的。请看下面的代码,它用到了一个新的方法 jQuery.when():
$.when($.ajax("test1.html"), $.ajax("test2.html")) .done(function(){ alert('success'); }) .fail(function(){ alert('error'); });
这段代码的意思是,先执行两个操作$.ajax("test1.html")和$.ajax("test2.html"),如果都成功了,就运行done()指定的回调函数;如果有一个失败或都失败了,就执行fail()指定的回调函数。
$.when() 方法的使用具体 参见文档。
普通操作的回调函数接口
deferred 对象的最大优点,就是它把这一套回调函数接口,从ajax操作扩展到了所有操作。也就是说,任何一个操作----不管是ajax操作还是本地操作,也不管是异步操作还是同步操作----都可以使用deferred对象的各种方法,指定回调函数。我们来看一个具体的例子,为一个很耗时的操作 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('success'); }) .fail(function () { alert('error'); });
另一种做法是直接将 wait 函数传入
$.Deferred()
:$.Deferred(wait) .done(function(){ alert('success'); }) .fail(function(){ alert('error'); });
jQuery 规定,
$.Deferred()
可以接受一个函数名(注意,是函数名)作为参数,$.Deferred()
所生成的 deferred 对象将作为这个函数的默认参数。更具体的信息请参见 阮一峰的文档。
deferred 对象的方法
描述: 一个工厂函数,这个函数返回一个链式实用对象,用返回对象方法来在回调队列中注册多个回调, 调用回调队列,传递任何同步或异步函数的成功或失败状态。$.Deferred()
生成一个 deferred 对象。jQuery.Deferred( [beforeStart ] )
工厂函数创建一个新的deferred对象。beforeStart
: 类型Function( Deferred deferred )
,一个构造函数返回之前调用的函数。jQuery.Deferred
方法可以传递一个可选的函数, 这个函数在方法返回之前调用,并且会把新的 deferred(延迟)对象作为 this 对象,将其作为第一个参数传递给函数。例如,被调用的函数可以使用deferred.then()
绑定回调。deferred.done()
指定操作成功时的回调函数。deferred.fail()
指定操作失败时的回调函数。deferred.promise()
没有参数时,返回一个新的 deferred。 对象,该对象的运行状态无法被改变;接受参数时,作用为在参数对象上部署 deferred 接口。
一个 Deferred(延迟)对象开始于 pending 状态。 任何回调使用deferred.resolve()
手动改变 deferred 对象的运行状态为"已完成",从而立即触发done()
方法。deferred.then()
,deferred.always()
,deferred.done()
, 或者deferred.fail()
添加到这个对象都是排队等待执行。调用deferred.resolve()
转换 Deferred(递延)到 resolved(解决)的状态,并立即执行设置中任何的 doneCallbacks。调用deferred.reject()
转换 Deferred(递延)到 rejected(拒绝)的状态,并立即执行设置中任何的 failCallbacks。一旦对象已经进入了解决或拒绝状态,它处于该状态。回调仍然可以添加到解决或拒绝 Deferred(递延)- 他们会立即执行。$.ajax()
返回的 jqXHR 对象 会根据请求返回的结果,自动改变自身的执行状态。但是,对于其他通过$.Deferred()
方法生成的 deferred 对象,它们的执行状态必须由程序员手动指定,由代码决定在什么时候触发回调函数。deferred.reject()
这个方法与deferred.resolve()
正好相反,调用后将 deferred 对象的运行状态变为"已失败",从而立即触发fail()
方法。$.when()
为多个操作指定回调函数。deferred.then()
方法
有时为了省事,可以把done()
和fail()
合在一起写,这就是then()
方法。$.when($.ajax( '/main.php' )) .then(successFunc, failureFunc);
如果
then()
有两个参数,那么第一个参数是done()
方法的回调函数,第二个参数是fail()
方法的回调方法。如果then()
只有一个参数,那么等同于done()
。deferred.always()
方法
这个方法也是用来指定回调函数的,它的作用是,不管调用的是deferred.resolve()
还是deferred.reject()
,最后总是执行。$.ajax( 'test.html' ) .always( function() { alert('已执行!');} );
更多信息请参见 jQuery API中文文档。