jQuery.queue源码分析

做者:禅楼望月( http://www.cnblogs.com/yaoyinglong


队列是一种特殊的线性表,它的特殊之处在于他只容许在头部进行删除,在尾部进行插入。经常使用来表示先进先出的操做(FIFO)--先进队列的元素先出队。
f1c60e3b-2978-442b-b660-74f86f25df7a
搜索整个jQuery库会发现,queue在jQuery内部仅供给animate动画来使用。它提供了对外的接口,所以程序员也可使用队列来完成一些特殊需求。
javascript

queue模块对外开放的API:
工具方法:
queue,dequeue,_queueHooks(仅内部使用)
实例方法:
queue,dequeue,delay,clearQueue,promisehtml

1 基本用法

jQuery的队列面向的是函数,若是你传递进去的不是函数,jQuery在出队的时候会出错,由于出队的时候,jQuery会执行该函数。前端

它比Deferred更强大,能够同时支持多个异步操做。在jQuery源码中,jQuery主要是为了完成运动(animate)。在队列中默认的队列名字为fx,animate所用的队列名字就是它。因此,若是省略了队列的名字,就会将函数添加到fx队列中。java

1.1 queue

工具方法中有三个可选参数:程序员

  1. element:附加列队的DOM元素(不是**jQuery**元素);
  2. queueName:字符串值,包含序列的名称。默认是 fx, 标准的效果序列;
  3. callback:要添加进队列的函数,它还能够是一个函数数组。

① 建立队列,并添加一个函数web

 
 
 
 
function callback1() { console.log("callback1");}$.queue($("#div1")[0],"q1",callback1);

② 获取队列
当只有两个参数的时候,表示获取element下queueName名称的队列
数组

 
 
 
 
console.log($.queue($("#div1")[0],"q1"));

4f85b751-7fb9-4f0d-93c5-51f415b3f4fa
③ 添加数组形式
还能够一次性传递一个数组形式的参数
promise

 
 
 
 
$.queue($("#div1")[0],"q1",callback1);$.queue($("#div1")[0],"q1",[callback2,callback3]);

可是,这时并非向现有的队列中继续添加[callback1,callback2],而是先将队列清空再添加[callback2,callback3];缓存

 
 
 
 
console.log($.queue($("#div1")[0],"q1"));

816872b8-915d-40de-a553-29f7ac16d467

1.2 dequeue

从队列最前端移除一个队列函数,并执行它。所以当回调有N个时,须要调用.dequeue方法N次元素才所有出列。.dequeue方法N次元素才所有出列。.dequeue的第一个参数是dom元素,第二个参数是queueName安全

 
 
 
 
function callback1() {console.log("callback1");}function callback2() {console.log("callback2");}function callback3() {console.log("callback3");}var div1=document.getElementById("div1");$.queue(div1,"q1",[callback1,callback2,callback3]);setInterval(function () {$.dequeue(div1,"q1");},2000);

这些回调函数的上下文是dom元素,参数是next函数和hooks对象,那next函数和hooks对象到底是什么东西呢?

 
 
 
 
function callback1(next,hook) { console.log(this); console.log(next); console.log(hook);}var div1=document.getElementById("div1");$.queue(div1,"q1",callback1);$.dequeue(div1,"q1");

112e50f6-5234-49e3-8f49-7b9a0f1cd655
能够看出执行next函数就是继续调用dequeue,进行下一个回调函数的出队操做。那么咱们就能够这样顺序调用全部的回调函数:

 
 
 
 
function callback1(next,hook) {console.log("callback1");next();}function callback2(next,hook) {console.log("callback2");next();}function callback3(next,hook) {console.log("callback3");next();}var div1=document.getElementById("div1");$.queue(div1,"q1",[callback1,callback2,callback3]);$.dequeue(div1,"q1");

8b034955-1afe-4fdf-be2e-c011b50ca84b
而hooks是一个对象字面量,里面的封装了一个Deferred。主要用于当队列里全部的callback都执行完后(此时startLength为0)进行最后的一个清理工做:

上面是工具和实例都有的方法,它们的用法出了工具方法第一个参数为DOM元素而实例方法不须要这个参数外,其余的都同样。下面方法是实例特有的。

1.3 promise

等队列执行完执行的内容。

 
 
 
 
<div id="div1" style="position:absolute; width: 100px; height: 100px; background: #BEE3D1"></div><script> $(function () { $("#div1").click(function () { $(this).animate({width:300},2000).animate({left:300},2000); $(this).promise().done(function () { alert(123); }) }) });</script>

1.4 delay

延迟后续添加的callback的执行,第一个参数time是延迟时间(另可以使用"slow"和"fast"),第二个是队列名。

 
 
 
 
function callback1(next,hook) {console.log("callback1");next();}function callback2(next,hook) {console.log("callback2");next();}function callback3(next,hook) {console.log("callback3");next();}var div1=$("#div1");div1.queue("q1",callback1) .delay("slow","q1") .queue("q1",callback2) .delay("1500","q1") .queue("q1",callback3);div1.dequeue("q1");

1.5 clearQueue

顾名思义,清空全部队列。

2 源码分析

2.1 queue

 
 
 
 
queue: function( elem, type, data ) { var queue; if ( elem ) {//在elem不为空的状况下进行操做 //这里为传进来的type添加一个“queue”后缀,当咱们没有传type时,默认使用“fx” type = ( type || "fx" ) + "queue"; //jQuery的队列是以Data为基础的。它是以缓存的形式添加在DOM元素或者Object上的。 //第一次进来queue为undefined queue = data_priv.get( elem, type ); // Speed up dequeue by getting out quickly if this is just a lookup //若是data存在,则往队列中添加元素(function,这里并无限制data为函数,应该限制一下,这样更安全)。 if ( data ) { //若是队列不存在,或者data为数组时 if ( !queue || jQuery.isArray( data ) ) { queue = data_priv.access( elem, type, jQuery.makeArray(data) ); } else { //若是有,而且data不为数组时,为queue数组添加data queue.push( data ); } } //返回该队列数组或者空数组。 return queue || []; }}

分析queue方法完源码后,咱们来为前面的测试代码(建立队列,并添加队员)画一个存储示意图:
bef9c9e7-fafb-4755-88c2-0ed7ae780986

2.2 _queueHooks

它是一个私有的方法,jQuery不建议外部来使用它。

 
 
 
 
_queueHooks: function( elem, type ) { var key = type + "queueHooks"; //若是data_priv中已经为elem存储了标识为key缓存,则返回它。或者为elem,的标识key,设置缓存。 return data_priv.get( elem, key ) || data_priv.access( elem, key, { //该缓存为一个对象字面量,里面属性empty为Callbacks对象,该延迟对象有被添加了一个方法, // 用来删除elem的type缓存,和key缓存 empty: jQuery.Callbacks("once memory").add(function() { data_priv.remove( elem, [ type + "queue", key ] ); }) });}

执行完它后,上面的示意图将变为这样:
37cd6dcf-3f85-4359-a257-4f2be521ffd3

2.3 dequeue

 
 
 
 
dequeue: function( elem, type ) { //type没有被传递,则使用“fx” type = type || "fx"; var queue = jQuery.queue( elem, type ),//获取elem下type队列 startLength = queue.length,//队列的长度 fn = queue.shift(),//让队列第一个队员出队 hooks = jQuery._queueHooks( elem, type ),//执行_queueHooks,添加一个延迟对象。 next = function() {//设置next方法。该方法是再执行下一个队员的出栈 jQuery.dequeue( elem, type ); }; //若是fn是"inprogress"则继续取下一个函数执行。对应startLength也--。这里主要是为了考 // 虑animate的实现。等到animate在看。 if ( fn === "inprogress" ) { fn = queue.shift(); startLength--; } if ( fn ) { //若是是默认的type,则往队列中添加一个"inprogress"字符串。 // 用于防止fx队列自动出栈。 if ( type === "fx" ) { queue.unshift( "inprogress" ); } // clear up the last queue stop function delete hooks.stop; //从这里咱们就知道fn必须为函数,不然就会出错。执行环境为elem,两个参数next和hooks fn.call( elem, next, hooks ); } //若是队员已经出栈完而且hooks被成功设置。 if ( !startLength && hooks ) { //触发Callbacks的fire,Callbacks中全部的方法执行。这里就是删除相关队列。 hooks.empty.fire(); }}

结合_queueHooksdequeue就能看出回调函数里的第二个参数hooks对象是什么了。它是一个Callbacks每次执行回调函数的最后都会检测队列长度是否为空,是的话执行Callbacks执行fire。执行了fireCallbacks中的回调函数就会执行。这是type队列,和type+queueHooks队列被删除了。
acb8d980-07ed-473f-a55e-e3bca04965db

下面是实例方法:
其中
queue,dequeue,clearQueue原码很是简单,在此不作分析。

2.4 delay

 
 
 
 
delay: function( time, type ) { //time能够是具体的毫秒数,也能够为jQuery.fx.speeds中定义的slow,fast,_default time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; type = type || "fx"; //往type队列中添加一个定时器。等过指定的时间后再去执行next函数。进行下一个队员出栈。 return this.queue( type, function( next, hooks ) { var timeout = setTimeout( next, time ); //为hooks添加了一个中止定时器的接口。用于中止该延迟,注意只能在Timeout触发以前中止。 hooks.stop = function() { clearTimeout( timeout ); }; });}

若是执行了hooks.stop,队列出队则会中止,后面的函数将不会被出队了。

2.5 promise

 
 
 
 
promise: function( type, obj ) { var tmp, count = 1, defer = jQuery.Deferred(),//建立一个延迟对象 elements = this,//elements为当前选集 i = this.length,//选集中元素的数量 resolve = function() { if ( !( --count ) ) { defer.resolveWith( elements, [ elements ] ); } }; if ( typeof type !== "string" ) { obj = type; type = undefined; } type = type || "fx"; while( i-- ) { //获取每一个元素的hooks。 tmp = data_priv.get( elements[ i ], type + "queueHooks" ); if ( tmp && tmp.empty ) { count++; //这里为每一个选项的hooks添加了一个回调函数,这个回调函数里面去触发延迟对象的 // resolveWith方法。从而造成一个闭包。而hooks方法只有在选项的队列的全部队员 // 都出栈后才去执行。因此,向这个延迟对象中添加的任何函数都会在全部队员出栈完 // 毕后背执行。 tmp.empty.add( resolve ); } } resolve(); //返回一个外部能不改变状态的延迟对象。 return defer.promise( obj );}

队列牵扯到了Callbacks和Deferred,所以须要对这两个知识点很是熟悉。

相关文章
相关标签/搜索