$('input')
.animate({left:'+=200px',top:'300'},2000)
.animate({left:'-=200px',top:20},1000)
.queue(function(){
// 这里dequeue将首先执行队列中的后一个函数,因此alert("y")
$(this).dequeue();
alert('x');
})
.queue(function(){
alert("y");
// 如果不主动dequeue, 队列执行就中断了,不会自动继续下去.
$(this).dequeue();
});
A. 利用jQuery内置的selector机制自然表达对一个集合的处理.
B. 使用Map表达多个属性变化
C. 利用微格式表达领域特定的差量概念. '+=200px'表示在现有值的基础上增加200px
D. 利用函数调用的顺序自动定义animation执行的顺序: 在后面追加到执行队列中的动画自然要等前面的动画完全执行完毕之后再启动.
jQuery动画队列的实现细节大概如下所示,
A. animate函数实际是调用queue(function(){执行结束时需要调用dequeue,否则不会驱动下一个方法})
queue函数执行时, 如果是fx队列, 并且当前没有正在运行动画(如果连续调用两次animate,第二次的执行函数将在队列中等待),则会自动触发dequeue操作, 驱动队列运行.
如果是fx队列, dequeue的时候会自动在队列顶端加入"inprogress"字符串,表示将要执行的是动画.
B. 针对每一个属性,创建一个jQuery.fx对象。然后调用fx.custom函数(相当于start)来启动动画。
C. custom函数中将fx.step函数注册到全局的timerFuncs中,然后试图启动一个全局的timer.
timerId = setInterval( fx.tick, fx.interval );
D. 静态的tick函数中将依次调用各个fx的step函数。step函数中通过easing计算属性的当前值,然后调用fx的update来更新属性。
E. fx的step函数中判断如果所有属性变化都已完成,则调用dequeue来驱动下一个方法。
很有意思的是, jQuery的实现代码中明显有很多是接力触发代码: 如果需要执行下一个动画就取出执行, 如果需要启动timer就启动timer等. 这是因为js程序是单线程的,真正的执行路径只有一条,为了保证执行线索不中断, 函数们不得不互相帮助一下. 可以想见, 如果程序内部具有多个执行引擎, 甚至无限多的执行引擎, 那么程序的面貌就会发生本质性的改变. 而在这种情形下, 递归相对于循环而言会成为更自然的描述.
9. promise模式:因果关系的识别
现实中,总有那么多时间线在独立的演化着, 人与物在时空中交错,却没有发生因果. 软件中, 函数们在源代码中排着队, 难免会产生一些疑问, 凭什么排在前面的要先执行? 难道没有它就没有我? 让全宇宙喊着1,2,3齐步前进, 从上帝的角度看,大概是管理难度过大了, 于是便有了相对论. 如果相互之间没有交换信息, 没有产生相互依赖, 那么在某个坐标系中顺序发生的事件, 在另外一个坐标系中看来, 就可能是颠倒顺序的. 程序员依葫芦画瓢, 便发明了promise模式.
promise与future模式基本上是一回事,我们先来看一下java中熟悉的future模式.
复制代码 代码如下:
futureResult = doSomething();
...
realResult = futureResult.get();
发出函数调用仅仅意味着一件事情发生过, 并不必然意味着调用者需要了解事情最终的结果. 函数立刻返回的只是一个将在未来兑现的承诺(Future类型), 实际上也就是某种句柄. 句柄被传来传去, 中间转手的代码对实际结果是什么,是否已经返回漠不关心. 直到一段代码需要依赖调用返回的结果, 因此它打开future, 查看了一下. 如果实际结果已经返回, 则future.get()立刻返回实际结果, 否则将会阻塞当前的执行路径, 直到结果返回为止. 此后再调用future.get()总是立刻返回, 因为因果关系已经被建立, [结果返回]这一事件必然在此之前发生, 不会再发生变化.
future模式一般是外部对象主动查看future的返回值, 而promise模式则是由外部对象在promise上注册回调函数.
复制代码 代码如下:
function getData(){
return $.get('/foo/').done(function(){
console.log('Fires after the AJAX request succeeds');
}).fail(function(){
console.log('Fires after the AJAX request fails');
});
}
function showDiv(){
var dfd = $.Deferred();
$('#foo').fadeIn( 1000, dfd.resolve );
return dfd.promise();
}
$.when( getData(), showDiv() )
.then(function( ajaxResult, ignoreResultFromShowDiv ){
console.log('Fires after BOTH showDiv() AND the AJAX request succeed!');
// 'ajaxResult' is the server's response
});