隊列是一種特殊的線性表,它的特殊之處在於他只容許在頭部進行刪除,在尾部進行插入。經常使用來表示先進先出的操做(FIFO)--先進隊列的元素先出隊。
搜索整個jQuery庫會發現,queue在jQuery內部僅供給animate動畫來使用。它提供了對外的接口,所以程序員也可使用隊列來完成一些特殊需求。javascript
queue模塊對外開放的API:
工具方法:queue
,dequeue
,_queueHooks
(僅內部使用)
實例方法:queue
,dequeue
,delay
,clearQueue
,promise
html
jQuery的隊列面向的是函數,若是你傳遞進去的不是函數,jQuery在出隊的時候會出錯,由於出隊的時候,jQuery會執行該函數。前端
它比Deferred更強大,能夠同時支持多個異步操做。在jQuery源碼中,jQuery主要是爲了完成運動(animate)。在隊列中默認的隊列名字爲fx,animate所用的隊列名字就是它。因此,若是省略了隊列的名字,就會將函數添加到fx隊列中。java
工具方法中有三個可選參數:程序員
① 建立隊列,並添加一個函數web
function callback1() { console.log("callback1");}$.queue($("#div1")[0],"q1",callback1);
② 獲取隊列
當只有兩個參數的時候,表示獲取element下queueName名稱的隊列數組
console.log($.queue($("#div1")[0],"q1"));
③ 添加數組形式
還能夠一次性傳遞一個數組形式的參數promise
$.queue($("#div1")[0],"q1",callback1);$.queue($("#div1")[0],"q1",[callback2,callback3]);
可是,這時並非向現有的隊列中繼續添加[callback1,callback2],而是先將隊列清空再添加[callback2,callback3];緩存
console.log($.queue($("#div1")[0],"q1"));
從隊列最前端移除一個隊列函數,並執行它。所以當回調有N個時,須要調用.dequeue
方法N次元素才所有出列。.dequeue
方法N次元素才所有出列。.dequeue
的第一個參數是dom元素,第二個參數是queueName安全
function callback1() {console.log("callback1");}function callback2() {console.log("callback2");}function callback3() {console.log("callback3");}var div1=document.getElementById("div1");$.queue(div1,"q1",[callback1,callback2,callback3]);setInterval(function () {$.dequeue(div1,"q1");},2000);
這些回調函數的上下文是dom元素,參數是next函數和hooks對象,那next函數和hooks對象到底是什麼東西呢?
function callback1(next,hook) { console.log(this); console.log(next); console.log(hook);}var div1=document.getElementById("div1");$.queue(div1,"q1",callback1);$.dequeue(div1,"q1");
能夠看出執行next函數就是繼續調用dequeue
,進行下一個回調函數的出隊操做。那麼咱們就能夠這樣順序調用全部的回調函數:
function callback1(next,hook) {console.log("callback1");next();}function callback2(next,hook) {console.log("callback2");next();}function callback3(next,hook) {console.log("callback3");next();}var div1=document.getElementById("div1");$.queue(div1,"q1",[callback1,callback2,callback3]);$.dequeue(div1,"q1");
而hooks是一個對象字面量,裏面的封裝了一個Deferred。主要用於當隊列裏全部的callback都執行完後(此時startLength爲0)進行最後的一個清理工做:
上面是工具和實例都有的方法,它們的用法出了工具方法第一個參數爲DOM元素而實例方法不須要這個參數外,其餘的都同樣。下面方法是實例特有的。
等隊列執行完再執行的內容。
<div id="div1" style="position:absolute; width: 100px; height: 100px; background: #BEE3D1"></div><script> $(function () { $("#div1").click(function () { $(this).animate({width:300},2000).animate({left:300},2000); $(this).promise().done(function () { alert(123); }) }) });</script>
延遲後續添加的callback的執行,第一個參數time是延遲時間(另可以使用"slow"和"fast"),第二個是隊列名。
function callback1(next,hook) {console.log("callback1");next();}function callback2(next,hook) {console.log("callback2");next();}function callback3(next,hook) {console.log("callback3");next();}var div1=$("#div1");div1.queue("q1",callback1) .delay("slow","q1") .queue("q1",callback2) .delay("1500","q1") .queue("q1",callback3);div1.dequeue("q1");
顧名思義,清空全部隊列。
queue: function( elem, type, data ) { var queue; if ( elem ) {//在elem不爲空的狀況下進行操做 //這裏爲傳進來的type添加一個「queue」後綴,當咱們沒有傳type時,默認使用「fx」 type = ( type || "fx" ) + "queue"; //jQuery的隊列是以Data爲基礎的。它是以緩存的形式添加在DOM元素或者Object上的。 //第一次進來queue爲undefined queue = data_priv.get( elem, type ); // Speed up dequeue by getting out quickly if this is just a lookup //若是data存在,則往隊列中添加元素(function,這裏並無限制data爲函數,應該限制一下,這樣更安全)。 if ( data ) { //若是隊列不存在,或者data爲數組時 if ( !queue || jQuery.isArray( data ) ) { queue = data_priv.access( elem, type, jQuery.makeArray(data) ); } else { //若是有,而且data不爲數組時,爲queue數組添加data queue.push( data ); } } //返回該隊列數組或者空數組。 return queue || []; }}
分析queue
方法完源碼後,咱們來爲前面的測試代碼(建立隊列,並添加隊員)畫一個存儲示意圖:
它是一個私有的方法,jQuery不建議外部來使用它。
_queueHooks: function( elem, type ) { var key = type + "queueHooks"; //若是data_priv中已經爲elem存儲了標識爲key緩存,則返回它。或者爲elem,的標識key,設置緩存。 return data_priv.get( elem, key ) || data_priv.access( elem, key, { //該緩存爲一個對象字面量,裏面屬性empty爲Callbacks對象,該延遲對象有被添加了一個方法, // 用來刪除elem的type緩存,和key緩存 empty: jQuery.Callbacks("once memory").add(function() { data_priv.remove( elem, [ type + "queue", key ] ); }) });}
dequeue: function( elem, type ) { //type沒有被傳遞,則使用「fx」 type = type || "fx"; var queue = jQuery.queue( elem, type ),//獲取elem下type隊列 startLength = queue.length,//隊列的長度 fn = queue.shift(),//讓隊列第一個隊員出隊 hooks = jQuery._queueHooks( elem, type ),//執行_queueHooks,添加一個延遲對象。 next = function() {//設置next方法。該方法是再執行下一個隊員的出棧 jQuery.dequeue( elem, type ); }; //若是fn是"inprogress"則繼續取下一個函數執行。對應startLength也--。這裏主要是爲了考 // 慮animate的實現。等到animate在看。 if ( fn === "inprogress" ) { fn = queue.shift(); startLength--; } if ( fn ) { //若是是默認的type,則往隊列中添加一個"inprogress"字符串。 // 用於防止fx隊列自動出棧。 if ( type === "fx" ) { queue.unshift( "inprogress" ); } // clear up the last queue stop function delete hooks.stop; //從這裏咱們就知道fn必須爲函數,不然就會出錯。執行環境爲elem,兩個參數next和hooks fn.call( elem, next, hooks ); } //若是隊員已經出棧完而且hooks被成功設置。 if ( !startLength && hooks ) { //觸發Callbacks的fire,Callbacks中全部的方法執行。這裏就是刪除相關隊列。 hooks.empty.fire(); }}
結合_queueHooks
和dequeue
就能看出回調函數裏的第二個參數hooks對象是什麼了。它是一個Callbacks
每次執行回調函數的最後都會檢測隊列長度是否爲空,是的話執行Callbacks
執行fire
。執行了fire
,Callbacks
中的回調函數就會執行。這是type隊列,和type+queueHooks隊列被刪除了。
下面是實例方法:
其中queue
,dequeue
,clearQueue
原碼很是簡單,在此不作分析。
delay: function( time, type ) { //time能夠是具體的毫秒數,也能夠爲jQuery.fx.speeds中定義的slow,fast,_default time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; type = type || "fx"; //往type隊列中添加一個定時器。等過指定的時間後再去執行next函數。進行下一個隊員出棧。 return this.queue( type, function( next, hooks ) { var timeout = setTimeout( next, time ); //爲hooks添加了一箇中止定時器的接口。用於中止該延遲,注意只能在Timeout觸發以前中止。 hooks.stop = function() { clearTimeout( timeout ); }; });}
若是執行了hooks.stop,隊列出隊則會中止,後面的函數將不會被出隊了。
promise: function( type, obj ) { var tmp, count = 1, defer = jQuery.Deferred(),//建立一個延遲對象 elements = this,//elements爲當前選集 i = this.length,//選集中元素的數量 resolve = function() { if ( !( --count ) ) { defer.resolveWith( elements, [ elements ] ); } }; if ( typeof type !== "string" ) { obj = type; type = undefined; } type = type || "fx"; while( i-- ) { //獲取每一個元素的hooks。 tmp = data_priv.get( elements[ i ], type + "queueHooks" ); if ( tmp && tmp.empty ) { count++; //這裏爲每一個選項的hooks添加了一個回調函數,這個回調函數裏面去觸發延遲對象的 // resolveWith方法。從而造成一個閉包。而hooks方法只有在選項的隊列的全部隊員 // 都出棧後纔去執行。因此,向這個延遲對象中添加的任何函數都會在全部隊員出棧完 // 畢後背執行。 tmp.empty.add( resolve ); } } resolve(); //返回一個外部能不改變狀態的延遲對象。 return defer.promise( obj );}
隊列牽扯到了Callbacks和Deferred,所以須要對這兩個知識點很是熟悉。