Queue队列:
队列是一种特殊的线性表,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队)。队列的特点是先进先出(FIFO-first in first out),即最先插入的元素最先被删除。
jQuery提供了jQuery.queue/dequeue和jQuery.fn.queue/dequeue,实现对队列的入队、出队操作。不同于队列定义的是,jQuery.queue和jQuery.fn.queue不仅执行出队操作,返回队头元素,还会自动执行返回的队头元素。
queue是用来维护函数队列的。比较常用的是queue(queueName, callback);其中queueName缺省是fn,即标准函数队列。 每个Element可以拥有多个队列,但是基本上都只使用到一个,即默认的fn队列。队列允许一系列函数被异步地调用而不会阻塞程序。 例如:$("#foo").slideUp().fadeIn();其实这个就是我们大家常用的链式调用,实际上这是一个Queue。所以队列和Deferred地位类似, 是一个内部使用的基础设施。当slideUp运行时,fadeIn被放到fx队列中,当slideUp完成后,从队列中被取出运行。queue函数允许 直接操作这个链式调用的行为。同时,queue可以指定队列名称获得其他能力,而不局限于fx队列
1 // 一般用法:
2 $("#foo").slideUp(function() {
3 alert("Animation complete.");
4 });
5 // 相当于:
6 $("#foo").slideUp(); // 不提供回调,只是触发事件
7 $("#foo").queue(function() { // 把回调函数加入
8 alert("Animation complete.");
9 $(this).dequeue(); // 必须从队列中取出,那么队列中的下一个函数就有机会被调用
10 });
调用时,如果不传入队列名,则默认为fx(标准动画)
1>>队列用数组实现,入队直接调用数组对象的方法 push入队
2>>入队必须是函数,或函数数组
3>>所有队列名加上queue后缀,表示这是一个队列
4>>如果传入的是数组,覆盖现有队列
5>>如果不是数组,则直接入队
1. 调用jQuery. dequeue出队时,会先调用jQuery.queue取得整个队列,因为队列用数组实现,可以调用数组的shift方法取出第一个元素并执行
2. 执行第一个元素时采用function.call( context, args ),由此可以看出jQuery队列只支持函数(这么说不完全准确,fx动画是个特例,
会在队列头端插入哨兵inprogress,类型为字符串)
3. 出队的元素会自动执行,无论这个元素是不是函数,如果不是函数此时就会抛出异常(这个异常并没有处理)
4. 如果队列变成空队列,则用关键delete删除jQuery.cache中type对应的属性
示例:
1. 先入队3个弹窗函数,分别弹出1、2、3
1 $('body').queue( 'test', function(){ alert(1); } )
2 $('body').queue( 'test', function(){ alert(2); } )
3 $('body').queue( 'test', function(){ alert(3); } )
2. 查看jQuery.data为body分配的唯一id(为什么要查看body的唯一id,请参考数据缓存的解析)
1 $.expando : "jQuery161017518149125935123"
2
3 command: >>> $('body')[0][$.expando]
$('body')[0]["jQuery161017518149125935123"]
$.expando有三部分构成:字符串"jQuery" + 版本号jQuery.fn.jquery + 随机数Math.random(),因此每次加载页面后都不相同。
3. 查看jQuery.cache对属性5对应的数据,格式化如下:
1 {
2 "1" : { ... },
3 "2" : { ... },
4 "3" : { ... },
5 "4" : { ... },
6 "5" : {
7 "jQuery161017518149125935123" : {
8 "testqueue" : [(function () {alert(1);}),(function () {alert(2);}),(function () {alert(3);})]
9 }
10 }
11 }
内部数据存储在$.expando属性("jQuery161017518149125935123")中,这点区别于普通数据
4.连续3次调用出队$('body').dequeue( 'test' ),
每次调用dequeue后用$('body').queue('test').length检查队列长度 控制台命令
1 $('body').dequeue( 'test' );
2 console.log($('body').dequeue( 'test' ).length);//2
3
4 $('body').dequeue( 'test' );
5 console.log($('body').dequeue( 'test' ).length);//1
6
7 $('body').dequeue( 'test' );
8 console.log($('body').dequeue( 'test' ).length);//0
调用出队函数dequeue后,入队的函数按照先进先出的顺序,依次被执行
5. 最后看看全部出队后,jQuery.cache中的状态
1 $.cache[5][$.expando]['testqueue'] //undefined
可以看到,testqueue属性已经从body的缓存中移除
jQuery.queue(element,[queueNmae]):返回在指定的元素element上将要执行的函数队列
jQuery.queue(element,queueName,newQueue or callback):修改在指定的元素element上将要执行的函数队列
使用jQuery.queue()添加函数后,最后要调用jQuery.queue(),使得下一个函数能线性执行
调用jQuery.data 存储为内部数据(pvt 为true)
1 queue:function(elem,type,data){
2 "jQuery161017518149125935123" : {
3 "testqueue" : [(function () {alert(1);}),(function () {alert(2);}),(function () {alert(3);})]
4 }
queue内部使用data或者JavaScript数组API来保存数据。其中操作数组的push和shift天生就是一组队列API。而data可以用来保存任意数据。
源码分析:
1 jQuery.extend({
2 //计数器 用在animate中
3 _mark:function(elem,type){
4 if(elem){
5 type = (type || "fx") + "mark";
6 //取出数据加1 存储在内部对象上
7 jQuery.data(elem,type,(jQuery.data(elem,type,undefined,true) || 0)+1,true);
8 }
9 },
10 //用在animate中 减一
11 _unmark:function(force,elem,type){
12 if(force !==true){
13 type = elem;
14 elem = force;
15 force = false;
16 }
17 if(elem){
18 type = type || "fx";
19 var key = type + "mark",
20 count = force ? 0 :((jQuery.data(elem,key,undefined,true) || 1) -1);
21 if(count){
22 jQuery.data(elem,key,count,true);
23 }else{
24 jQuery.removeData(elem,key,true);
25 handleQueueMarkDefer(elem,type,"mark");
26 }
27 }
28 },
29 //elem必须存在
30 if(elem){
31 //默认fn:为type或者fx type相当于例子中的test
32 type = (type || "fx") + "queue";//属性名加上queue
33 }
34 //取出队列 data 内部API:data(elem,key,value,pvt);
35 //这里不传入value 只是取队列。
36 var q = jQuery.data(elem,type,undefined,true);
37 //如果data存在,才会进行后边的转换数组 入队等操作
38 if(data){
39 //队列不存在或者data是数组 可以makeArray转换
40 if(!q || jQuery.isArray(data){
41 //数组实现队列 type的属性值为jQuery.makeArray(data)数组
42 q = jQuery.data(elem,type,jQuery.makeArray(data),true);
43 }else{
44 //如果不是数组,则直接入队
45 q.push(data);
46 }
47 }
48 //返回队列 即入队同时返回整个队列
49 //简洁实用的避免空引用的技巧
50 return q || [];
51 },
52 //出队并执行 调用jQuery.queue取的整个队列 再调用shift取出第一个元素来执行
53 dequeue:function(elem,type){
54 type = type || "fx";//默认为fx 入队被改为fxqueue了?
55 //得到这个队列 执行这句 var q = jQuery.data(elem,type,undefined,true);
56 var queue = jQuery.queue(elem,type),//取出队列
57
58 fn = queue.shift(),//取出第一个 shift():用于把数组的第一个元素从其中删除,并返回第一个元素的值
59 defer;
60 //"inprogress"岗哨 如果第一个元素时岗哨
61 if(fn === "inprogress"){//如果取出来的fn是一个正在执行中的标准动画fx,抛弃执行哨兵(inprogress) 重新取
62 fn = queue.shift();//这里是取第二个作为fn的值,因为第一个取到的是正在执行的fx
63 }
64
65 if(fn){
66 //如果是标准动画,则在队列头部增加处理中哨兵属性,阻止fx自动执行
67 if(type === "fx"){
68 //在队列头部增加哨兵 inprogress unshift():可向数组的开头添加一个或更多元素
69 queue.unshift("inprogress");//标准动画 在第一个位置插入inprogress
70 }
71 //fn函数
72 fn.call(elem,function(){
73 //这个回调函数不会自动执行 没有return
74 jQuery.dequeue(elem,type);
75 });
76 }
77 if(!queue.length){//删除缓存的数据
78 jQuery.removeData(elem,type + "queue",true);//内部使用delete删除type对应的空数组
79 handleQueueMarkDefer(elem,type,"queue");
80 }
81 }
82 });
83 jQuery.fn.extend({
84 //queue([queueName])返回指定元素上将要执行的函数队列
85 //queue([queueName],callback)修改指定元素上将要执行的函数队列
86 queue:function(type,data){
87 //只传了一个非字符串的参数 则默认为动画fx
88 if(typeof type !== "string"){
89 data = type;
90 type = "fx";
91 }
92 //data等于undefined 认为是取队列
93 if(data === undefined){//取值
94 return jQuery.queue(this[0],type);
95 }
96 //如果传入了data 在每一个匹配的元素上执行入队操作
97 return this.each(function(){//修改
98 var queue = jQuery.queue(this,type,data);
99 //如果动画执行完毕(且不是inprogress)从队列头部移除
100 if(type === "fx" && queue[0] !== "inprogree"){
101 jQuery.dequeue(this,type);//移除
102 }
103 });
104 },
105 //调用jQuery.dequeue出队
106 dequeue:function(type){
107 return this.each(function(){
108 jQuery.dequeue(this,type);
109 }
110 },
111 //延迟执行队列中未执行的函数,通过在队列中插入一个时延出队的函数来实现
112 delay:function(time,type){
113 time = jQuery.fx ? jQuery.fx.speeds[time] || time :time;
114 type = type || "fx";
115 return this.queue(type,function(){
116 var elem = this;
117 setTimeout(function(){
118 jQuery.dequeue(elem,type);
119 },time);
120 }
121 },
122 //清空队列,通过将第二个参数设为空数组[]
123 clearQueue:function(type){
124 return this.queue(type || "fx",[]);
125 },
126 //返回一个只读视图,当队列中指定类型的函数执行完毕后
127 promise:function(type,object){
128 //type不是字符串,值object是type
129 if(typeof type !== "string"){
130 object = type;
131 type = undefined;
132 }
133 type = type || "fx"; //取type的值 或者是fx
134 var defer = jQuery.Deferred(),
135 elements = this,
136 i = elements.length,
137 count =1,
138 deferDataKey = type + "defer",
139 queueDataKey = type + "queue",
140 markDataKey = type + "mark",
141 tmp;
142 function resolve(){
143 if(!(--count)){//如果count=0 执行队列回调函数函数使用指定上下文,context :elements,args:elements
144 defer.resolveWith(elements,[elements]);
145 }
146 }
147 while( i-- ) {
148 //延迟队列 或者执行队列 或者标记队列?
149 if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
150 ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
151 jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
152 jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) {
153 count++;
154 tmp.done( resolve );//
155 }
156 }
157 resolve();
158 return defer.promise();
159 }
160 });