• jQuery.queue源码分析


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


    队列是一种特殊的线性表,它的特殊之处在于他只允许在头部进行删除,在尾部进行插入。常用来表示先进先出的操作(FIFO)--先进队列的元素先出队。
    f1c60e3b-2978-442b-b660-74f86f25df7a
    搜索整个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"));

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

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

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

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

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

    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");

    112e50f6-5234-49e3-8f49-7b9a0f1cd655
    可以看出执行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");

    8b034955-1afe-4fdf-be2e-c011b50ca84b
    而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方法完源码后,我们来为前面的测试代码(创建队列,并添加队员)画一个存储示意图:
    bef9c9e7-fafb-4755-88c2-0ed7ae780986

    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. }

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

    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队列被删除了。
    acb8d980-07ed-473f-a55e-e3bca04965db

    下面是实例方法:
    其中
    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,因此需要对这两个知识点非常熟悉。

  • 相关阅读:
    反馈神经网络Hopfield网络
    读《数学之美》第三章 统计语言模型
    读《数学之美》第三章 统计语言模型
    C++标准模板库STL算法与自适应容器(栈和队列)
    C++标准模板库STL算法与自适应容器(栈和队列)
    数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树
    数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树
    c中常用的关键字static const volatile
    多媒体开发之---一个破解版的迅雷云点播网站
    多媒体开发之---h.264 rtsp网络流测试流
  • 原文地址:https://www.cnblogs.com/yaoyinglong/p/5747764.html
Copyright © 2020-2023  润新知