在$.when() 的实现中有趣的是,它并非仅能解析deferred对象,还可以传递不是deferred对象的参数,在处理的时候会把它们当做deferred对象并立 即执行回调(doneCallbacks)。 这也是jQuery的Deferred实现中值得一提的地方,此外,deferred.then()还为deferred.done和 deferred.fail()方法在deferred的队列中增加回调提供支持。
利用前面介绍的表中提到的deferred功能,我们来看一个代码示例。 在这里,我们创建一个非常基本的应用程序:通过$.get方法(返回一个promise)获取一条外部新闻源(1)并且(2)获取最新的一条回复。 同时程序还通过函数(prepareInterface())实现新闻和回复内容显示容器的动画。
为了确保在执行其他相关行为前,上面的这三个步骤确保完成,我们使用$.when()。根据您的需要 .then()和.fail() 处理函数可以被用来执行其他程序逻辑。
deferreds在ajax的幕后操作中使用并不意味着它们无法在别处使用。 在本节中,我们将看到在一些解决方案中,使用deferreds将有助于抽象掉异步的行为,并解耦我们的代码。
异步缓存当涉及到异步任务,缓存可以是一个有点苛刻的,因为你必须确保对于同一个key任务仅执行一次。因此,代码需要以某种方式跟踪入站任务。 例如下面的代码片段:
缓存机制需要确保 脚本不管是否已经存在于缓存,只能被请求一次。 因此,为了缓存系统可以正确地处理请求,我们最终需要写出一些逻辑来跟踪绑定到给定url上的回调。
值得庆幸的是,这恰好是deferred所实现的那种逻辑,因此我们可以这样来做:
代码相当简单:我们为每一个url缓存一个promise对象。 如果给定的url没有promise,我们创建一个deferred,并发出请求。 如果它已经存在我们只需要为它绑定回调。 该解决方案的一大优势是,它会透明地处理新的和缓存过的请求。 另一个优点是一个基于deferred的缓存 会优雅地处理失败情况。 当promise以‘rejected’状态结束的话,我们可以提供一个错误回调来测试:
$.cachedGetScript( url ).then( successCallback, errorCallback );
请记住:无论请求是否缓存过,上面的代码段都会正常运作!
通用异步缓存为了使代码尽可能的通用,我们建立一个缓存工厂并抽象出实际需要执行的任务:
现在具体的请求逻辑已经抽象出来,我们可以重新写cachedGetScript:
每次调用createCache将创建一个新的缓存库,并返回一个新的高速缓存检索函数。现在,我们拥有了一个通用的缓存工厂,它很容易实现涉及从缓存中取值的逻辑场景。
图片加载另一个候选场景是图像加载:确保我们不加载同一个图像两次,我们可能需要加载图像。 使用createCache很容易实现:
接下来的代码片段如下:
无论image.png是否已经被加载,或者正在加载过程中,缓存都会正常工作。
缓存数据的API响应哪些你的页面的生命周期过程中被认为是不可变的API请求,也是缓存完美的候选场景。 比如,执行以下操作:
程序允许你在Twitter上进行搜索,同时缓存它们:
基于deferred的缓存并不限定于网络请求;它也可以被用于定时目的。
例如,您可能需要在网页上给定一段时间后执行一个动作,来吸引用户对某个不容易引起注意的特定功能的关注或处理一个延时问题。 虽然setTimeout适合大多数用例,但在计时器出发后甚至理论上过期后就无法提供解决办法。 我们可以使用以下的缓存系统来处理:
新的afterDOMReady辅助方法用最少的计数器提供了domReady后的适当时机。 如果延迟已经过期,回调会被马上执行。
同步多个动画动画是另一个常见的异步任务范例。 然而在几个不相关的动画完成后执行代码仍然有点挑战性。尽管在jQuery1.6中才提供了在动画元素上取得promise对象的功能,但它是很容易的手动实现:
然后,我们可以使用$.when()同步化不同的动画:
我们也可以使用同样的技巧,建立了一些辅助方法:
然后想下面这样使用新的助手代码来同步动画:
一次性事件虽然jQuery提供你可能需要的所有的时间绑定方法,但当事件仅需要处理一次时,情况可能会变得有点棘手。( 与$.one() 不同 )
例如,您可能希望有一个按钮,当它第一次被点击时打开一个面板,面板打开之后,执行特定的初始化逻辑。 在处理这种情况时,人们通常会这样写代码:
不久后,你可能会在面板打开之后点击按钮时添加一些操作,如下:
这是一个非常耦合的解决办法。 如果你想添加一些其他的操作,你必须编辑绑定代码或拷贝一份。 如果你不这样做,你唯一的选择是测试buttonClicked。由于buttonClicked可能是false,新的代码可能永远不会被执行,因此你 可能会失去这个新的动作。
使用deferreds我们可以做的更好 (为简化起见,下面的代码将只适用于一个单一的元素和一个单一的事件类型,但它可以很容易地扩展为多个事件类型的集合):
该代码的工作原理如下:
代码虽然很冗长,但它会简化相关问题的处理。 让我们先定义一个辅助方法:
然后,之前的逻辑可以重构如下:
如果我们需要执行一些动作,只有当面板打开以后,所有我们需要的是这样的:
如果面板没有打开,行动将得到延迟到单击该按钮时。
组合助手单独看以上每个例子,promise的作用是有限的 。 然而,promise真正的力量是把它们混合在一起。
在第一次点击时加载面板内容并打开面板
假如,我们有一个按钮,可以打开一个面板,请求其内容然后淡入内容。使用我们前面定义的助手方法,我们可以这样做:
在第一次点击时载入图像并打开面板
假如,我们已经的面板有内容,但我们只希望当第一次单击按钮时加载图像并且当所有图像加载成功后淡入图像。HTML代码如下:
我们使用data-src属性描述图片的真实路径。 那么使用promise助手来解决该用例的代码如下: