jQuery源碼分析系列(38) : 隊列操做

Queue隊列,如同data數據緩存與Deferred異步模型同樣,都是jQuery庫的內部實現的基礎設施html

Queue隊列是animate動畫依賴的基礎設施,整個jQuery中隊列僅供給動畫使用前端

 

Queue隊列ajax

隊列是一種特殊的線性表,只容許在表的前端(隊頭)進行刪除操做(出隊),在表的後端(隊尾)進行插入操做(入隊)。隊列的特色是先進先出(FIFO-first in first out),即最早插入的元素最早被刪除。算法

 

爲何要引入隊列?編程

咱們知道代碼的執行流有異步與同步之分,例如後端

var a = 1;

setTimeout(function(){
    a = 2;
},0)

alert(a) //1

咱們一直習慣於「線性」地編寫代碼邏輯,可是在JavaScript編程幾乎老是伴隨着異步操做:數組

setTimeout,CSS3 Transition/Animation,ajax,dom的繪製,postmessage,Web Database等等,大量異步操做所帶來的回調函數,會把咱們的算法分解地支離破碎promise

以前咱們說過對於異步+回調的模式,怎麼「拉平」異步操做,使之跟同步同樣,由於異步操做進行流程控制的時候無非避免的要嵌套大量的回調邏輯,因此就會出現promises約定了緩存

那麼jQuery引入隊列其實從一個角度上能夠認爲:容許一系列函數被異步地調用而不會阻塞程序dom

$("#Aaron").slideUp().fadeIn()

這是jQuery的一組動畫鏈式序列,它的內部其實就是一組隊列Queue,因此隊列和Deferred地位相似, 是一個內部使用的基礎設施,當slideUp運行時,fadeIn被放到fx隊列中,當slideUp完成後,從隊列中被取出運行。queue函數容許 直接操做這個鏈式調用的行爲。同時,queue能夠指定隊列名稱得到其餘能力,而不侷限於fx隊列

 


jQuery提供了2組隊列操做的API:

  • jQuery.queue/dequeue
  • jQuery.fn.queue/dequeue

可是不一樣與普通隊列定義的是:jQuery.queue和jQuery.fn.queue不只執行出隊操做,返回隊頭元素,還會自動執行返回的隊頭元素

fn是擴展在原型上的高級API是提供給實例使用的,.queue/.dequeue, 其內部是調用的$.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); // 第三個參數爲function
$.queue(body, 'aa', cb2); 

//get
$.queue(body, 'aa')  //[function ,function]

這個方法有點類型get有點相似隊列的push操做,jQuery的方法的接口重載是很是嚴重的,常常同一個接口便是set也是get,無論符不符合基本原則,可是它卻很實用

無非就是把數據給緩存起來,爲何載體是一個jQuery對象,由於保存數據的手段是經過data數據緩存實現的

data_priv = new Data();
queue: function(elem, type, data) {
    var queue;
    if (elem) {
        type = (type || "fx") + "queue";
        queue = data_priv.get(elem, type);
        // Speed up dequeue by getting out quickly if this is just a lookup
        if (data) {
            if (!queue || jQuery.isArray(data)) {
                queue = data_priv.access(elem, type, jQuery.makeArray(data));
            } else {
                queue.push(data);
            }
        }
        return queue || [];
    }
},

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 queue = jQuery.queue(elem, type),
    startLength = queue.length,
    fn = queue.shift(),
    hooks = jQuery._queueHooks(elem, type),
    next = function() {
        jQuery.dequeue(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,因此咱們的代碼能夠這樣改

var body = $('body');
function cb1(next,hoost) {
    console.log(11111)
    next()  //執行了cb2 //22222
}

function cb2() {
    console.log(22222)
}

//set
$.queue(body, 'aa', cb1); // 第三個參數爲function
$.queue(body, 'aa', cb2); 

$.dequeue(body, 'aa')

next內部仍然調用$.dequeue,這樣能夠接着執行隊列中的下一個callback

$.dequeue裏的hooks是當隊列裏全部的callback都執行完後(此時startLength爲0)進行最後的一個清理工做

if ( !startLength && hooks ) {
    hooks.empty.fire();
}

鉤子其實就是jQuery.Callbacks對象,能夠實現一個收集器的功能,至於在什麼狀況下時候,以後動畫中開始分析

 


因此隊列的本質是利用Array的push和shift來完成先進先出(First In First Out),可是這個方法的缺點也很明顯,沒法單獨作一個獨立的模塊處理,由於它必需要跟jQuery對象吻合,並且對傳遞的數據只能是函數

相關文章
相關標籤/搜索