7-2 queue与dequeue
本节编程练习不计算学习进度,请电脑登录imooc.com操作

queue与dequeue

$.queue : 显示或操作匹配的元素上已经执行的函数列队
这个方法有两个作用,它既是 setter,又是 getter。第一个参数 elem 是 DOM 元素,第二个参数 type 是字符串,第三个参数 data 可以是 function 或数组。
var body = $('body');
function cb1() {alert(1)}
function cb2() {alert(2)}

//set,第三个参数是函数
$.queue(body, 'aa', cb1); 
$.queue(body, 'aa', cb2); 

//get
$.queue(body, 'aa')  //[function ,function]
这个方法有点类型 get 有点类似队列的 push 操作,jQuery 的方法的接口重载是非常严重的,经常同一个接口即是 set 也是 get,不管符不符合基本原则,但是它却很实用。无非就是把数据给缓存起来,为什么载体是一个jQuery对象,因为保存数据的手段是通过data数据缓存实现的。
queue: function(elem, type, data) {
   //参考右边实现
}
data 与 jQuery 对象之间是通过 uuid 建立了一个无耦合的映射关系,具体可以翻阅之前的关于“数据缓存”,源码有一个默认处理 type = (type || "fx") + "queue" 可见是专职供fx动画队列处理的。

$.dequeue : 匹配的元素上执行队列中的下一个函数
var body = $('body');
function cb1() {console.log(11111)}
function cb2() {console.log(22222)}
//set
$.queue(body, 'aa', cb1); // 第三个参数为function
$.queue(body, 'aa', cb2);

$.dequeue(body, 'aa')  //11
$.dequeue(body, 'aa')  //22
出列就有点类似 shift 的操作,但是不同的是还会执行这个 cb1 与 cb2,将回调函数出列执行,每调用一次仅出列一个,因此当回调有 N 个时,需要调用 $.dequeue 方法 N 次元素才全部出列,来看看源码:
var dequeue = jQuery.queue(elem, type),
  //参考右边实现
};
知道原理了, 这个就很简单了,通过 queue 的 get 取出队列的所有数据,判断一下长度,然后截取出第一个,然后做好一个预处理生成下一个的 next。
 
这里有一个hooks?

仔细分析下这个内部 queueHooks
_queueHooks: function(elem, type) {
    var key = type + "queueHooks";
    return data_priv.get(elem, key) || data_priv.access(elem, key, {
        empty: jQuery.Callbacks("once memory").add(function() {
            data_priv.remove(elem, [type + "queue", key]);
        })
    });
}
我们说了dequeue不仅是取出来还需要执行,在执行的时候把next与hooks传递给外部的回调,这就是js的逻辑上的很绕的地方,在内部可以传递一个引用出去,又能提供外部调用或者执行。
fn.call(elem, next, hooks)
因为传递了next,所以我们的代码可以参考右边代码,next内部仍然调用$.dequeue,这样可以接着执行队列中的下一个 callback,$.dequeue 里的 hooks 是当队列里所有的 callback 都执行完后(此时 startLength 为0)进行最后的一个清理工作。
if ( !startLength && hooks ) {
    hooks.empty.fire();
}
钩子其实就是 jQuery.Callbacks 对象,可以实现一个收集器的功能,至于在什么情况下时候,之后动画中开始分析,所以队列的本质是利用 Array 的 push 和 shift 来完成先进先出(First In First Out),但是这个方法的缺点也很明显,无法单独做一个独立的模块处理,因为它必须要跟 jQuery 对象吻合,而且对传递的数据只能是函数。

任务

  1. <!doctype html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
  5. <script src="http://code.jquery.com/jquery-latest.js"></script>
  6. <title></title>
  7. </head>
  8. <body>
  9.  
  10. <button id="test1">$.queue</button>
  11. <button id="test2">模拟queue</button>
  12. <button id="test3">$.dequeue</button>
  13. <button id="test4">模拟dequeue</button>
  14. <ul></ul>
  15.  
  16. <script type="text/javascript">
  17.  
  18. /**
  19. * 显示或操作匹配的元素上已经执行的函数列队。
  20. */
  21. function queue(elem, type, data) {
  22. var queue;
  23.  
  24. if (elem) {
  25. type = (type || "fx") + "queue";
  26. //获取队列数据
  27. //data_priv.get(elem, type);
  28. queue = $(elem).data(type);
  29. // 如果传递了数据,这存在缓存中
  30. if (data) {
  31. if (!queue || jQuery.isArray(data)) {
  32. //data_priv.access
  33. queue = $.data(elem, type, jQuery.makeArray(data))
  34. } else {
  35. queue.push(data);
  36. }
  37. }
  38. return queue || [];
  39. }
  40. }
  41.  
  42. function dequeue(elem, type) {
  43. type = type || "fx";
  44.  
  45. //取出队列
  46. var queue = jQuery.queue(elem, type),
  47. startLength = queue.length,
  48. //取出第一个队列
  49. fn = queue.shift(),
  50. hooks = jQuery._queueHooks(elem, type),
  51. next = function() {
  52. jQuery.dequeue(elem, type);
  53. };
  54.  
  55. // If the fx queue is dequeued, always remove the progress sentinel
  56. if (fn === "inprogress") {
  57. fn = queue.shift();
  58. startLength--;
  59. }
  60.  
  61. if (fn) {
  62.  
  63. // Add a progress sentinel to prevent the fx queue from being
  64. // automatically dequeued
  65. // 如果开始执行,就需要加一个进程锁
  66. // 如果是dequeue操作, 去掉锁, 执行队列里的函数, 同时给队列加上锁.
  67. // 如果是queue操作, 要看锁的状态, 如果被锁上了, 就只执行队列的添加操作. 不再调用dequeue.
  68. if (type === "fx") {
  69. queue.unshift("inprogress");
  70. }
  71.  
  72. // clear up the last queue stop function
  73. delete hooks.stop;
  74. fn.call(elem, next, hooks);
  75. }
  76.  
  77. if (!startLength && hooks) {
  78. hooks.empty.fire();
  79. }
  80. }
  81.  
  82.  
  83.  
  84. var body = $('body')[0];
  85.  
  86. function cb1() {
  87. alert(1)
  88. }
  89.  
  90. function cb2() {
  91. alert(2)
  92. }
  93.  
  94.  
  95. function clear(){
  96. $('body').removeData();
  97. }
  98.  
  99. $("#test1").click(function() {
  100. $.queue(body, 'aa', cb1);
  101. $.queue(body, 'aa', cb2);
  102. alert($.queue(body, 'aa'))
  103. })
  104.  
  105. $("#test2").click(function() {
  106. clear()
  107. queue(body, 'aa', cb1);
  108. queue(body, 'aa', cb2);
  109. alert(queue(body, 'aa'))
  110. })
  111. $("#test3").click(function() {
  112. //set
  113. $.queue(body, 'aa', cb1); // 第三个参数为function
  114. $.queue(body, 'aa', cb2);
  115. alert($.dequeue(body, 'aa')) //11
  116. alert($.dequeue(body, 'aa')) //11
  117. })
  118.  
  119. $("#test4").click(function() {
  120. show(ajaxConvertXML(xml))
  121. })
  122.  
  123.  
  124.  
  125.  
  126.  
  127. </script>
  128. </body>
  129. </html>
下一节