3.9 jQuery的Deferred对象
在进行JavaScript
开发的过程中,经常会遇到要执行耗时任务的情况,该任务可能是远程Ajax
调用,也可能是本地的耗时操作,总之不能立即有结果。遇到这种情况,通常就需要采用回调函数来监听该耗时操作的执行情况,当耗时操作执行完成后,自动激发相应的回调函数。jQuery
为解决这类问题提供了Deferred
对象。
3.9.1 jQuery的异步调用
在jQuery
以前的版本中,当我们使用$.ajax()
方法进行Ajax
调用时,通常的代码格式如下:
1 | $.ajax({ |
在该Ajax
调用中通过success
和error
分别指定了调用成功、调用失败时的回调函数,这是jQuery
传统的管理回调函数的方式,这种方式虽然简单、直观,但编程一致性并不好,因为调用不同耗时操作时可能需要通过不同选项来指定回调函数。Deferred
对象则改变了这种局面,Deferred
对象允许用”一致”的代码来管理回调函数。例如通过done()
方法添加回调成功的回调函数,通过fail()
方法添加调用失败的回调函数……从jQuery 1.5
开始,$.ajax()
方法返回的就是Deferred
对象,因此jQuery
允许我们采用如下形式来完成Ajax
调用。
下面的示例使用Deferred
对象改写了前面的Ajax
示例,程序如下。
1 |
|
该程序调用了$.ajax()
方法来发送异步请求,该方法将会返回一个Deferred
对象,接下来通过Deferred
对象的done()
方法和fail()
方法分别添加调用成功、调用失败的回调函数。
提示:通过上面的示例不难看出jQuery
中Deferred
的设计:
Deferred
其实就是JavaScript
新增的Promise
对象。它们都可对所有”耗时操作”的回调函数进行统一管理,就像jQuery
最初提供的jQuery
对象一样。jQuery
对象对所有DOM
元素进行了统一管理,从而提供了一致的编程模型。而Deferred
对象或Promise
对象则可对所有耗时操作进行统一管理。因此jQuery
完全可能对原有的Ajax
进行全新设计,从传统编程模型全面过渡到使用Deferred
来管理回调函数。
Deferred详解
如何创建Deferred对象
除了可以调用$.ajax()
方法返回Deferred
对象之外,jQuery
专门提供了jQuery.Deferred()
方法来创建Deferred
对象。
Deferred提供的3个耗时状态
Deferred
对象提供了以下三种状态来表示耗时操作的执行结果:
pending
状态:pending
表示任务执行中,未完成。此时将执行Deferred
对象progress()
方法添加的回调函数。fulfilled
状态:fulfilled
表示任务完成了,该任务会执行resolve
函数;进而执行Deferred
对象done()
方法添加的回调函数。rejected
状态:rejected
表示任务失败了,该任务会执行reject
函数;进而执行fail()
方法添加的回调函数。
修改Deferred状态的方法
为了让开发者手动改变Deferred
对象的状态,Deferred
还提供了reject(args)
和resolve(args)
两个方法,其中
reject(args)
方法用于将Deferred
对象改为”任务失败
“状态,resolve(args)
方法用于将Deferred
对象改为”任务完成
“状态。
Deferred对象的方法详解
Deferred
对象支持的方法如下。
监听操作的方法
方法 | 描述 |
---|---|
progress(progressCallbacks) |
指定在Deferred 对象包含的异步操作执行过程中 激发的回调函数。 |
done(doneCallbacks) |
指定Deferred 对象包含的异步操作执行成功后激发 的回调函数。 |
fail(doneCallbacks) |
指定Deferred 对象包含的异步操作执行失败后激发 的回调函数。 |
always(alwaysCallbacks [,alwaysCallbacks]) |
该方法相当于done() 、fail() 两个方法的合体。也就是说,无论Deferred 对象包含的异步操作执行成功还是执行失败,总会激发该方法指定的回调函数。 |
then(doneCallbacks,failCallbacks [,progressCallbacks]) |
then() 方法相当于done() 、fail() 、和progress() 三个方法的综合版本,该方法可以分别为Deferred 对象包含的异步操作在完成时 、失败时 、进行中 指定一个或多个回调函数。 |
调用监听操作方法的方法
方法 | 描述 |
---|---|
notify(args) |
调用在Deferred 对象上通过progress() 方法添加的函数,args 作为参数传入progress() 方法添加的函数。 |
notifyWith(context [,args]) |
类似于notify(args) 方法的功能,只是激发progress() 方法添加的函数时将传入context 和args 参数。 |
reject(args) |
将Deferred 对象状态改为”rejected (任务已失败)”,在Deferred 对象上通过fail() 方法添加的函数将会被激发,args 作为参数传入fail() 方法添加的函数。 |
rejectWith(context [,args]) |
类似于reject(args) 方法的功能,只是激发fail() 方法添加的函数时将传入context 和args 参数。 |
resolve(args) |
将Deferred 对象状态改为”rejected (任务已完成)”,在Deferred 对象上通过done() 方法添加的函数将会被激发,args 作为参数传入done() 方法添加的函数。 |
resolveWith(context [,args]) |
类似于resolve(args) 方法的功能,只是激发done() 方法添加的函数时将传入context 和args 参数。 |
返回Promise对象的方法
方法 | 描述 |
---|---|
promise([target]) |
返回Deferred 对象对应的Promise 对象(它相当于Deferred 对象的副本,不允许开发者通过Promise 对象修改Deferred 对象的状态)。如果指定了target 参数,则会在该参数指定的对象上增加Deferred 接口。 |
#### 查询一步操作所处的执行状态 #### |
方法 | 描述 |
---|---|
state() |
返回Deferred 对象包含的异步操作所处的执行状态。 |
该方法返回表示执行状态的字符串,可能返回如下三个字符串。 | |
1. pending :任务执行中,未完成。 |
|
2. resolved :任务完成。 |
|
3. rejected :任务失败。 |
|
程序示例
下面的程序定义了一个耗时操作。程序需要统计出指定范围的所有质数
,由于这个过程耗时较长,因此考虑使用Deferred
对象来管理回调函数。JavaScript
代码如下。
1 |
|
该程序中①号代码创建了一个Deferred
对象,该对象用于标识该耗时任务的完成进度:
- 当任务完成时,程序调用
Deferred
对象的resolve()
方法将它的状态设为resolved
; - 当任务出错时,程序调用
Deferred
对象的reject()
方法将它的状态设为rejected
。
calPrime()
方法返回Deferred
对象的Promise
对象,
Promise
对象相当于Deferred
对象的副本,但程序不能通过Promise
对象来改变Deferred
对象的状态—通过这种方式,即可避免别人在方法外改变Deferred
对象的状态。
Deferred
对象用于表示calPrime()
耗时操作的完成状态,因此通常只应该在该方法内改变Deferred
对象的状态。为了保证效果,建议将Deferred
对象定义成该方法的局部变量,并让该方法返回Deferred
对象的Promise
对象。
在浏览器中执行该JavaScript
代码,即可看到当calPrime()
耗时操作执行完成后,会自动回调done()
方法指定的回调函数。