標籤:style class blog code http java
Queue隊列,如同data資料緩衝與Deferred非同步模型一樣,都是jQuery庫的內部實現的基礎設施
Queue隊列是animate動畫依賴的基礎設施,整個jQuery中隊列僅供給動畫使用
Queue隊列
隊列是一種特殊的線性表,只允許在表的前端(隊頭)進行刪除操作(出隊),在表的後端(隊尾)進行插入操作(入隊)。隊列的特點是先進先出(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等等,大量非同步作業所帶來的回呼函數,會把我們的演算法分解地支離破碎
之前我們說過對於非同步+回調的模式,怎麼“拉平”非同步作業,使之跟同步一樣,因為非同步作業進行流程式控制制的時候無非避免的要嵌套大量的回調邏輯,所以就會出現promises約定了
那麼jQuery引入隊列其實從一個角度上可以認為:允許一系列函數被非同步地調用而不會阻塞程式
$("#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對象吻合,而且對傳遞的資料只能是函數