参考链接
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 开始,$.ajax() 返回的 jqXHR 对象 本身就是 deferred 对象,因此可以像上面代码中那样进行链式调用。
从 jQuery 1.5 开始,$.ajax() 返回的 jqXHR 对象 实现了 Promise 接口, 使它拥有了 Promise 的所有属性,方法和行为。(见 Deferred object 获取更多信息)。为了让回调函数的名字统一,便于在 $.ajax() 中使用。jqXHR 也提供.error() .success() 和.complete() 方法。这些方法都带有一个参数,该参数是一个函数,此函数在 $.ajax() 请求结束时被调用, 并且这个函数接收的参数,与调用 $.ajax() 函数时的参数是一致。这将允许你在一次请求时,对多个回调函数进行赋值,甚至允许你在请求已经完成后,对回调函数进行赋值 ( 如果该请求已经完成,则回调函数会被立刻调用)。
注意事项: 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.resolve() 手动改变 deferred 对象的运行状态为 ” 已完成 ”,从而立即触发 done() 方法。
一个 Deferred(延迟)对象开始于 pending 状态。任何回调使用 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 中文文档。