讀jQuery之二十一(隊列queue)


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,如果不存在則初始化為一個空對象,然后將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,3
setInterval(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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM