queue模組在jQuery中分在Effects中,搜尋整個庫會發現queue也僅在特效模組effects.js中被使用。jQuery抽取出獨立的命名空間給queue,說明除了內部Effects模組使用外,用戶端程式員可以充分發揮聰明才智使用queue來構建非動畫API。
queue模組向外開放的API分別是
- 掛在$上的$.queue、$.dequeue、$._queueHooks(僅內部)
- 掛在jQuery對象上的有queue、dequeue、delay、clearQueue、promise
按照jQuery的慣例,掛在$上的方法屬於低級API,掛在jQuery對象上的才是經常使用的。
一般低級API是為進階API服務的,即 queue內部會使用$.queue, dequeue內部會使用$.dequeue。這裡實際是實現為一個隊列,$.queue是入列,$.dequeue是出列。
一、$.queue
這個方法有兩個作用,它既是setter,又是getter。第一個參數elem是DOM元素,第二個參數type是字串,第三個參數data可以是function或數組。
1. 設定指定名字的queue
function cb1() {alert(1)}function cb2() {alert(2)}var arr = [cb1, cb2];$.queue(el, 'mx', cb1); // 第三個參數為function$.queue(el, 'xm', arr); // 第三個參數為數組
2. 這時可以取到存入的callbacks
var cbs1 = $.queue(el, 'mx'); // [cb1]var cbs2 = $.queue(el, 'xm'); // [cb1, cb2]
$.queue內部使用 $._data方法,將資料儲存下來。預設type/queueName使用 "fx" + queue。$.queue的實現很簡單,代碼不過15行,即取緩衝對象queue,如果不存在則初始化為一個Null 物件,然後將data存入,如果存在則直接將data push到數組中。
二、$.dequeue
將回呼函數出列執行,每調用一次僅出列一個,因此當回調有N個時,需要調用$.dequeue方法N次元素才全部出列。$.dequeue的第一個參數是dom元素,第二個參數是queueName
function ff1() {console.log(1)}function ff2() {console.log(2)}function ff3() {console.log(3)}var p = $('p')[0];$.queue(p, 'mx1', ff1);$.queue(p, 'mx1', ff2);$.queue(p, 'mx1', ff3);// 每2秒調用一次$.dequeue,依次輸出1,2,3setInterval(function() {$.dequeue(p, 'mx1') }, 2000);
回呼函數的上下文是dom元素,參數是next函數和hooks對象
var p = $('p')[0];function func(next, hooks) { console.log(this); console.log(next); console.log(hooks);}$.queue(p, 'mx', func);$.dequeue(p, 'mx'); // p, function, [object Object]
next內部仍然調用$.dequeue,這樣可以接著執行隊列中的下一個callback。$.dequeue裡的hooks是當隊列裡所有的callback都執行完後(此時startLength為0)進行最後的一個清理工作,
if ( !startLength && hooks ) {hooks.empty.fire();}
hooks.empty是一個jQuery.Callbacks對象,而它則是定義在$._queueHooks裡
_queueHooks: function( elem, type ) {var key = type + "queueHooks";return jQuery._data( elem, key ) || jQuery._data( elem, key, {empty: jQuery.Callbacks("once memory").add(function() {jQuery._removeData( elem, type + "queue" );jQuery._removeData( elem, key );})});}
以上就是queue的全部了,本質是利用Array的push和shift來完成先進先出(First In First Out),但這裡有個缺陷,jQuery的queue從1.1開始就是為effects模組服務的,因此queue裡存的都是function。個人覺得如果只存function,應該對data參數做個嚴格類型判斷,如果不是function則拋異常。但目前的版本沒有做嚴格判斷,如果我存的不是function,這樣dequeue時會報錯。如下
var p = $('p')[0];$.queue(p, 'mx1', {}); // 注意第三個參數是對象,非function$.dequeue(p, 'mx1'); // fn.call 報錯,因為fn不是function
三、queue
知道了$.queue,queue就很好理解了,它無非是內部調用了下$.queue。queue比$.queue 少了第一個參數,內部使用this代替第一個參數。
function ff1() {console.log(1)}function ff2() {console.log(2)}function ff3() {console.log(3)}var $p = $('p');$p.queue('mx', ff1);$p.queue('mx', ff2);$p.queue('mx', ff3);
這樣,三個function就入列了,列名是"mx"。 取隊列元素只需傳一個列名如"mx"
var queue = $p.queue('mx'); // [ff1, ff2, ff3]
還有個技巧就是,如果使用jQuery預設的隊列"fx",可以只傳data
function ff1() {console.log(1)}function ff2() {console.log(2)}function ff3() {console.log(3)}var $p = $('p');$p.queue(ff1);$p.queue(ff2);$p.queue(ff3);
另外一點,當使用預設列名"fx"時,它會調用$.dequeue出列執行下,源碼如下
if ( type === "fx" && queue[0] !== "inprogress" ) {jQuery.dequeue( this, type );}
四、dequeue
dequeue則更是未添加任何特殊處理,直接調用的$.dequeue,見源碼
dequeue: function( type ) {return this.each(function() {jQuery.dequeue( this, type );});},
五、delay
delay用來延遲後續添加的callback的執行,第一個參數time是延遲時間(另可使用"slow"和"fast"),第二個是隊列名。
function cb() { console.log(1); }var $p = $('p');$p.delay(2000, 'mx').queue('mx', cb); $p.dequeue('mx'); // 2秒後輸出1
如果是這樣
function ff1() {console.log(1)}function ff2() {console.log(2)}var $p = $('p');$p.queue('mx', ff1);$p.delay(4000, 'mx');$p.queue('mx', ff2);$p.dequeue('mx'); // 立即輸出1$p.dequeue('mx'); // 4秒後輸出2
六、clearQueue
顧名思義,清空所有隊列。沒什麼好說的,源碼如下,直接使用一個空數組覆蓋之前的數組隊列了。
clearQueue: function( type ) {return this.queue( type || "fx", [] );},
七、promise
這個方法返回一個promise對象,promise對象既是前面提到的Deferred對象的閹割版。你可以使用done、fail、progress添加,但不能觸發。用在queue模組裡有特殊意義,比如done它指queue裡所有function都執行後才執行done添加的。如
function ff1() {alert(1)}function ff2() {alert(2)}function succ() {alert('done')}$body = $('body')$body.queue('mx', ff1);$body.queue('mx', ff2);var promise = $body.promise('mx');promise.done(succ);setInterval(function() {$body.dequeue('mx') // 先彈出1,2,最後是"done"}, 1500)
註:閱讀版本為1.9.1