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()方法指定的回调函数。