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

队列是一种特殊的线性表,它的特殊之处在于他只允许在头部进行删除,在尾部进行插入。常用来表示先进先出的操作(FIFO)--先进队列的元素先出队。

搜索整个jQuery库会发现,queue在jQuery内部仅供给animate动画来使用。它提供了对外的接口,因此程序员也可以使用队列来完成一些特殊需求。

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

1 基本用法

jQuery的队列面向的是函数,如果你传递进去的不是函数,jQuery在出队的时候会出错,因为出队的时候,jQuery会执行该函数。

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

1.1 queue

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

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

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

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

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

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


③ 添加数组形式
还可以一次性传递一个数组形式的参数

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

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

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

1.2 dequeue

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

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

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

  1. function callback1(next,hook) {
  2. console.log(this);
  3. console.log(next);
  4. console.log(hook);
  5. }
  6. var div1=document.getElementById("div1");
  7. $.queue(div1,"q1",callback1);
  8. $.dequeue(div1,"q1");


可以看出执行next函数就是继续调用dequeue,进行下一个回调函数的出队操作。那么我们就可以这样顺序调用所有的回调函数:

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


而hooks是一个对象字面量,里面的封装了一个Deferred。主要用于当队列里所有的callback都执行完后(此时startLength为0)进行最后的一个清理工作:

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

1.3 promise

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

  1. <div id="div1" style="position:absolute; width: 100px; height: 100px; background: #BEE3D1"></div>
  2. <script>
  3. $(function () {
  4. $("#div1").click(function () {
  5. $(this).animate({width:300},2000).animate({left:300},2000);
  6. $(this).promise().done(function () {
  7. alert(123);
  8. })
  9. })
  10. });
  11. </script>

1.4 delay

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

  1. function callback1(next,hook) {console.log("callback1");next();}
  2. function callback2(next,hook) {console.log("callback2");next();}
  3. function callback3(next,hook) {console.log("callback3");next();}
  4. var div1=$("#div1");
  5. div1.queue("q1",callback1)
  6. .delay("slow","q1")
  7. .queue("q1",callback2)
  8. .delay("1500","q1")
  9. .queue("q1",callback3);
  10. div1.dequeue("q1");

1.5 clearQueue

顾名思义,清空所有队列。

2 源码分析

2.1 queue

  1. queue: function( elem, type, data ) {
  2. var queue;
  3. if ( elem ) {//在elem不为空的情况下进行操作
  4. //这里为传进来的type添加一个“queue”后缀,当我们没有传type时,默认使用“fx”
  5. type = ( type || "fx" ) + "queue";
  6. //jQuery的队列是以Data为基础的。它是以缓存的形式添加在DOM元素或者Object上的。
  7. //第一次进来queue为undefined
  8. queue = data_priv.get( elem, type );
  9. // Speed up dequeue by getting out quickly if this is just a lookup
  10. //如果data存在,则往队列中添加元素(function,这里并没有限制data为函数,应该限制一下,这样更安全)。
  11. if ( data ) {
  12. //如果队列不存在,或者data为数组时
  13. if ( !queue || jQuery.isArray( data ) ) {
  14. queue = data_priv.access( elem, type, jQuery.makeArray(data) );
  15. } else {
  16. //如果有,并且data不为数组时,为queue数组添加data
  17. queue.push( data );
  18. }
  19. }
  20. //返回该队列数组或者空数组。
  21. return queue || [];
  22. }
  23. }

分析queue方法完源码后,我们来为前面的测试代码(创建队列,并添加队员)画一个存储示意图:

2.2 _queueHooks

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

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

执行完它后,上面的示意图将变为这样:

2.3 dequeue

  1. dequeue: function( elem, type ) {
  2. //type没有被传递,则使用“fx”
  3. type = type || "fx";
  4. var queue = jQuery.queue( elem, type ),//获取elem下type队列
  5. startLength = queue.length,//队列的长度
  6. fn = queue.shift(),//让队列第一个队员出队
  7. hooks = jQuery._queueHooks( elem, type ),//执行_queueHooks,添加一个延迟对象。
  8. next = function() {//设置next方法。该方法是再执行下一个队员的出栈
  9. jQuery.dequeue( elem, type );
  10. };
  11. //如果fn是"inprogress"则继续取下一个函数执行。对应startLength也--。这里主要是为了考
  12. // 虑animate的实现。等到animate在看。
  13. if ( fn === "inprogress" ) {
  14. fn = queue.shift();
  15. startLength--;
  16. }
  17. if ( fn ) {
  18. //如果是默认的type,则往队列中添加一个"inprogress"字符串。
  19. // 用于防止fx队列自动出栈。
  20. if ( type === "fx" ) {
  21. queue.unshift( "inprogress" );
  22. }
  23. // clear up the last queue stop function
  24. delete hooks.stop;
  25. //从这里我们就知道fn必须为函数,否则就会出错。执行环境为elem,两个参数next和hooks
  26. fn.call( elem, next, hooks );
  27. }
  28. //如果队员已经出栈完并且hooks被成功设置。
  29. if ( !startLength && hooks ) {
  30. //触发Callbacks的fire,Callbacks中所有的方法执行。这里就是删除相关队列。
  31. hooks.empty.fire();
  32. }
  33. }

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

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

2.4 delay

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

如果执行了hooks.stop,队列出队则会停止,后面的函数将不会被出队了。

2.5 promise

  1. promise: function( type, obj ) {
  2. var tmp,
  3. count = 1,
  4. defer = jQuery.Deferred(),//创建一个延迟对象
  5. elements = this,//elements为当前选集
  6. i = this.length,//选集中元素的数量
  7. resolve = function() {
  8. if ( !( --count ) ) {
  9. defer.resolveWith( elements, [ elements ] );
  10. }
  11. };
  12. if ( typeof type !== "string" ) {
  13. obj = type;
  14. type = undefined;
  15. }
  16. type = type || "fx";
  17. while( i-- ) {
  18. //获取每个元素的hooks。
  19. tmp = data_priv.get( elements[ i ], type + "queueHooks" );
  20. if ( tmp && tmp.empty ) {
  21. count++;
  22. //这里为每个选项的hooks添加了一个回调函数,这个回调函数里面去触发延迟对象的
  23. // resolveWith方法。从而形成一个闭包。而hooks方法只有在选项的队列的所有队员
  24. // 都出栈后才去执行。所以,向这个延迟对象中添加的任何函数都会在所有队员出栈完
  25. // 毕后背执行。
  26. tmp.empty.add( resolve );
  27. }
  28. }
  29. resolve();
  30. //返回一个外部能不改变状态的延迟对象。
  31. return defer.promise( obj );
  32. }

队列牵扯到了Callbacks和Deferred,因此需要对这两个知识点非常熟悉。

jQuery.queue源码分析的更多相关文章

  1. jQuery.attributes源码分析(attr/prop/val/class)

    回顾 有了之前的几篇对于jQuery.attributes相关的研究,是时候分析jQuery.attr的源码了 Javascript中的attribute和property分析 attribute和p ...

  2. jQuery.buildFragment源码分析以及在构造jQuery对象的作用

    这个方法在jQuery源码中比较靠后的位置出现,主要用于两处.1是构造jQuery对象的时候使用 2.是为DOM操作提供底层支持,这也就是为什么先学习它的原因.之前的随笔已经分析过jQuery的构造函 ...

  3. jQuery.access源码分析

    基本理解 jQuery.attr是jQuery.attr,jQuery.prop,jQuery.css提供底层支持,jQuery里一个比较有特色的地方就是函数的重载, 比如attr,有如下几种重载 $ ...

  4. jQuery.Deferred 源码分析

    作者:禅楼望月(http://www.cnblogs.com/yaoyinglong ) 1 引子 观察者模式是我们日常开发中经常用的模式.这个模式由两个主要部分组成:发布者和观察者.通过观察者模式, ...

  5. java读源码 之 queue源码分析(PriorityQueue,附图)

    今天要介绍的是基础容器类(为了与并发容器类区分开来而命名的名字)中的另一个成员--PriorityQueue,它的大名叫做优先级队列,想必即使没有用过也该有所耳闻吧,什么?没..没听过?emmm... ...

  6. jQuery 2.1.4版本的源码分析

    jQuery 2.1.4版本的源码分析 jquery中获取元素的源码分析 jQuery.each({// 获取当前元素的父级元素 parent: function(elem) { var parent ...

  7. jQuery.Callbacks 源码解读二

    一.参数标记 /* * once: 确保回调列表仅只fire一次 * unique: 在执行add操作中,确保回调列表中不存在重复的回调 * stopOnFalse: 当执行回调返回值为false,则 ...

  8. jQuery 源码分析(十一) 队列模块 Queue详解

    队列是常用的数据结构之一,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队).特点是先进先出,最先插入的元素最先被删除. 在jQuery内部,队列模块为动画模块提供基 ...

  9. jQuery-1.9.1源码分析系列(十四) 一些jQuery工具

    为了给下一章分析动画处理做准备,先来看一下一些工具.其中队列工具在动画处理中被经常使用. jQuery.fn. queue(([ queueName ] [, newQueue ]) || ([ qu ...

随机推荐

  1. STM32串口接收不定长数据原理与源程序(转)

    今天说一下STM32单片机的接收不定长度字节数据的方法.由于STM32单片机带IDLE中断,所以利用这个中断,可以接收不定长字节的数据,由于STM32属于ARM单片机,所以这篇文章的方法也适合其他的A ...

  2. 《与mysql零距离接触》视屏学习笔记

    1.数据表的增删改查操作(crud): 对于表: 增:create  table  XXXX 删:drop table XXXX 改:alter table XXXX rename to XXXX 查 ...

  3. MediaWiki安装与配置(Ubuntu 10.4)

    实验室准备发布一个网站,本来是准备外包给别人做的,后来自己调研了一下,发现也没有想象的复杂和困难,于是最近一周自己吭哧吭哧地把网站搭好了. 之所以使用Mediawiki,一是考虑到是以实验室发布,不想 ...

  4. addEventListener、attachEvent、cancelBubble兼容性随笔

    一.前言 1. element.addEventListener(eventType, handler, capture); (1)参数eventType是要注册句柄的事件类型名. (2)参数hand ...

  5. GEF: 图形拖拽处理

    重写EditPart#getDragTracker 即可替换拖拽事件.

  6. 压力测试工具ab使用

    ab全名是apache bench,是apache自带的一款压力测试工具.它通过创建多个线程来模拟并发,测试目标是基于URL的,因此不论是什么web服务器都可以支持. 使用ab非常简单,进入apach ...

  7. dijit样式定制(三)Button、RadioButton、CheckBox

    dijit.form.DropDownButton dijit的button中除了ComboButton使用table布局外,其他的button都是用span嵌套布局,下图中可看一下button的主要 ...

  8. Asp.Net MVC 扩展 Html.ImageFor 方法详解

    背景: 在Asp.net MVC中定义模型的时候,DataType有DataType.ImageUrl这个类型,但htmlhelper却无法输出一个img,当用脚手架自动生成一些form或表格的时候, ...

  9. 在代码中设置IE9的默认文档模式

    要在旧系统中加一个jquery插件,本地demo测试没问题,部署到服务器后却报错.使用的是IE9浏览器,打开F12调试台,发现默认的文档模式是IE7,调成IE9后,报错消失.可以确认是该插件不兼容IE ...

  10. MySQL--将MySQL数据导入到SQL Server

    随着时代的进步,社会的发展,各种技术层出不穷五花八门乱七八糟数不胜数(写作文呢!!!) 不扯废话,简单而言,很多公司都会同时使用多种数据库,因此数据在不同数据库之间导入导出就成为一个让人蛋疼的问题,对 ...