$.when(deferreds)
1 引子
上一篇博文中介绍的Deferred,它表示一个延迟对象。但是很多时候,我们需要在多个延迟对象(异步代码)都执行完后再去执行另一段代码,这种情况下,使用Deferred就行不通了,就需要使用这里的$.when。
可以看出,要触发done,必须当所有的Deferred都触发resolve,而要触发fail,只要任意一个Deferred触发reject即可。$.when的用途就是来管理多个延迟对象,其他只传一个Deferred或者传递的不是延迟对象。都是没什么意义的,并不代表会出错哦。
2 源码解析
在开始源码分析之前,我们先想一想,让我们自己实现这个$.when,应该怎么来实现。首先它肯定是个延迟对象,然后呢,它要等所有的Deferred都触发resolve它才触发resolve,所以需要一个计数器,计数器初始值为参数的个数,某个Deferred触发resolve时,我们就让计数器减1,某个Deferred触发reject时,我们就将计数器置零。然后调用相应的done或者fail,即可。那我们来看jQuery值怎么实现的。
2.1 对象构建
逐行分析:
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0
当只有一个参数,判断它是不是Deferred,不是的话让计数器为0。其他情况计数器都等于参数的个数。这里忽略了其他参数不为Deferred的情况,而是将处理放在后面进行。
deferred = remaining === 1 ? subordinate : jQuery.Deferred()
如果目前检测出只有一个Deferred时(这种情况就是when的参数只有一个,并且为Deferred),when内部就是用这个Deferred。就如我上面所说的,这样做就没有任何意义了。只是让JS引擎多跑了几个弯而已。如果有多个Deferred,则创建一个新的Deferred。
接下来创建了一个名为updateFunc的函数,它是在执行期执行的,这里跳过。
if判断when有没有参数传递进来,其实这里没必要判断length,判断remaining就可以了。在if中,使用遍历when的所有参数。发现有不是Deferred的,立即--remaining。如果参数是Deferred,则为该Deferred的3个状态(resolve,reject,notify)分别注册函数。注册的这3个函数就是用来当传递进来的这些Deferred有相关动作的时候,让when的Deferred得到通知(--remaining或者触发done或者立即调用fail)。
if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
}
如果现在判断when的参数中没有Deferred是,直接触发when的resolveWith方法。因此就会触发done方法列表。
return deferred.promise();
返回一个精简版的Deferred。主要是为了防止在外部修改了when的Deferred的状态。
至此,when所对应的延迟对象构建成功。它是一个不能被修改状态的精简版Deferred。
2.2 执行期
执行期没什么内容,就是去执行upateFunc函数。
在前面构建期,为参数Deferred添加注册时间时,done,progress是这样的:
done( updateFunc( i, resolveContexts, resolveValues ) )
progress( updateFunc( i, progressContexts, progressValues ) )
然后upateFunc返回一个闭包。闭包中,首先配置values,配置触发resolveWith触发时的参数,这个参数会传递给每一个订阅者。然后判断,如果是progress传递进来的方法,则为触发的是notifyWith。计数器,不做变化,只是调用通知订阅者列表。否则,则先让计数器减减,再判断计数器是否为0,是则触发resolveWith方法。这里的deferred指的是when的Deferred。不能混淆了。
那如果,when的参数中有一个触发了reject呢?就会直接调用deferred的reject方法。
fail( deferred.reject )
因为这里给when的每个Deferred参数的reject添加了一个这样的订阅方法:立即出发deferred的reject方法。
总体来说,when的源码还是比较简单的。
作者:禅楼望月
出处:http://www.cnblogs.com/yaoyinglong
如果,您认为阅读这篇博文让您有些收获,不妨点击一下右下角的【推荐】
如果,您希望这篇博文也能帮助到其他人,不妨点击一下右边的【分享到…】
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】
如果,您对我的博客内容感兴趣,请继续关注我的后续博客,我是【禅楼望月】
本文版权归作者和博客园共有,欢迎转载,但请注明出处,且在文章页面明显位置给出原文连接。