Queue源码

一、queue模块的意义

处理动画事件的回调金字塔。

无queue模块:

$("#animate1").animate({heihgt:"760px"},function(){

        $("#animate2").animate({width:"1280px"},function(){

                  console.log("end");

        });

});

有queue模块:

var fns=[

      function(){$("#animate1").animate({heihgt:"760px"},dequeue},

      function(){$("#animate2").animate({width:"1280px"},dequeue},

      function(){console.log("end")},

]

$(document).queue("animate",fns);

function dequeue(){

      $(document).dequeue("animate")

};

dequeue();

二、实现思路

$.queue(ele,type,data)方法:构建Data对象存储queue函数队列data,以type为键附着于ele元素中。

$.dequeue(ele,type)方法:逐个取出queue函数队列并执行。

$._queueHooks(ele,type)方法:构建另一个Data对象hooks。queue函数队列执行完成清空Data对象,该函数存储在hooks.empty中。

queue执行函数传参为next和hooks,以启用下一个queue执行函数(另一种方法是调用dequeue方法逐次执行queue函数)、或者调用hooks中函数,比如$ele.delay调用hooks.stop执行延时等。

注:next函数使用同koa中间件、async,不同于jquery-deferred

$ele.queue(type,data)方法:注册、更新或获取queue函数队列,参数data须为数组形式的函数或纯函数。

$ele.dequeue(type)方法:逐次执行queue函数队列。

$ele.clearQueue(type)方法:清空queue函数队列。

$ele.delay(time,type)方法:$ele.queue方法注册queue函数队列时,经过延迟时间time后,调用next执行下一个queue函数。参数time可以是"show","fast",或毫秒数。

$ele.promise(type,obj)方法:返回deferred.promise对象,用以注册done回调函数,该回调函数在各元素的queue函数均执行完成后执行,通过将resolve启动器添加到hooks.empty中实现。

三、源码解读

define([
	"./core",
	"./data/var/dataPriv",// 私有对象Data,存储数据或函数
	"./deferred",// 延迟对象Deferred
	"./callbacks"// 回调对象Callbacks
],function(jQuery,dataPriv){

jQuery.extend({
	// 创建、更新或获取Data对象,Data对象通过dataPriv构造函数附着在elem上
	// 结合dequeue方法使用的时候需要保证data为数组形式的函数或函数形式
	// data为queue队列函数回调,参数为next,hooks,dequeue方法中约定
	queue:function(elem,type,data){
		var queue;

		if ( elem ){
			type=(type || "fx")+"queue";
			queue=dataPriv.get(elem,type);// 获取私有Data对象存储数据

			if ( data ){
				if ( !queue || jQuery.isArray(data) ){// 当data为数组格式时,创建、更新或获取私有Data对象
					queue=dataPriv.access(elem,type,jQuery.makeArray(data));
				}else{
					queue.push(data);
				}
			}
			return queue || [];
		}
	},

	// 获取queue方法注册的Data对象,逐个执行queue队列函数,全部执行完成时调用hooks.empty.fire
	// 下一个queue队列函数执行需要在queue方法注册的data中调用next函数参数
	dequeue:function(elem,type){
		type=type || "fx";

		var queue=jQuery.queue(elem,type),// 获取queue方法添加的data数据,可能为函数
			startLength=queue.length,
			fn=queue.shift(),// 获取queue方法添加的data数据首项
			hooks=jQuery._queueHooks(elem,type),
			next=function(){
				jQuery.dequeue(elem,type);
			};

		if ( fn==="inprogress" ){
			fn=queue.shift();
			startLength--;
		}

		if ( fn ){
			// 数组头部添加"inprogress",重用代码考虑;以"fx"作为type值注册的queue队列函数不允许自动执行
			if ( type==="fx" ){
				queue.unshift("inprogress");
			}

			delete hooks.stop;// 为fn创建的hooks.stop腾出空间,但是不执行???
			fn.call(elem,next,hooks);
		}

		// queue方法添加的data数据完毕后,执行_queueHooks注册的回调函数,清空queue模块创建的Data对象
		if ( !startLength && hooks ){
			hooks.empty.fire();
		}
	},

	// 获取或注册回调函数Data对象,附着在elem上
	// 获取后使用Callbacks对象的fire方法触发
	_queueHooks:function(elem,type){
		var key=type+"queueHooks";
		return dataPriv.get(elem,key) || dataPriv.access(elem,key,{
			empty:jQuery.Callbacks("once memory").add(function(){
				dataPriv.remove(elem,[type+"queue",key]);
			})
		});
	}
});

// 挂载在jquery对象上,便捷调用
jQuery.fn.extend({
	// 无参数或仅有一个type参数时,获取以type值注册的Data对象数据
	// 有type,没有data时,返回jquery元素对象
	// 既有type又有data时,更新以type值注册的Data数据,并设置hooks回调,
	// type类型为fx,调用$.dequeue执行data首项函数
	queue:function(type,data){
		var setter=2;

		// 处理参数
		if ( typeof type!=="string" ){
			data=type;
			type="fx";
			setter--;
		}

		if ( arguments.length<setter ){// 无参数或仅有一个type参数时
			return jQuery.queue(this[0],type);
		}

		return data===undefined ?
			this :
			this.each(function(){
				var queue=jQuery.queue(this,type,data);

				// 创建hooks存放回调函数,默认包含清空Data对象的empty回调函数,Callbacks的fire方法调用
				jQuery._queueHooks(this,type);

				// type类型为fx,执行queue函数队列首项函数
				if ( type==="fx" && queue[0]!=="inprogress" ){
					jQuery.dequeue(this,type);
				}
			});
	},

	// 调用执行queue方法注册的Data对象数据,即函数队列fn
	dequeue:function(type){
		return this.each(function(){
			jQuery.dequeue(this,type);
		});
	},

	// 清空queue方法注册的Data对象数据
	clearQueue:function(type){
		return this.queue(type || "fx",[]);
	},

	// 当前所有元素的queue队列函数执行完成后(dequeue方法触发执行),执行延迟对象的回调函数
	promise:function(type,obj){
		var tmp,
			count=1,
			defer=jQuery.Deferred(),
			elements=this,
			i=this.length,
			// defer成功时回调启动器,添加到hooks.empty回调中,所有元素的queue函数都执行完成后触发
			resolve=function(){
				if ( !(--count) ){
					defer.resolveWith(elements,[elements]);
				}
			};

		if ( typeof type!=="string" ){
			obj=type;
			type=undefined;
		}
		type=type || "fx";

		while (i--){
			tmp=dataPriv.get(elements[i], type+"queueHooks");
			if ( tmp && tmp.empty ){
				count++;
				tmp.empty.add(resolve);
			}
		}
		resolve();
		return defer.promise(obj);
	}
});

return jQuery;
});
define([
	"../core",
	"../queue",
	"../effects" // Delay is optional because of this dependency
],function(jQuery){

// 通过setTimeout函数延迟执行下一个queue队列函数
jQuery.fn.delay=function(time,type){
	time=jQuery.fx ? jQuery.fx.speeds[time] || time : time;// 延迟时间,slow、fast转化为毫秒数
	type=type || "fx";

	return this.queue(type,function(next,hooks){
		var timeout=window.setTimeout(next,time);
		hooks.stop=function(){
			window.clearTimeout(timeout);
		};
	});
};

return jQuery.fn.delay;
});

三、源码解读

function ff1() {
    console.log(1)
}
function ff2() {
    console.log(2)
}
function succ() {
    console.log('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)
 

相关推荐